style: align post detail discussion layout
This commit is contained in:
@@ -105,6 +105,7 @@
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { translatePost } from '@/api/community'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { formatCount } from '@/utils/number'
|
||||
|
||||
const props = defineProps<{ post: any }>()
|
||||
defineEmits(['tap', 'unlock', 'like', 'avatar-tap'])
|
||||
@@ -179,21 +180,6 @@ const formatTime = (value: any) => {
|
||||
return `${date.getMonth() + 1}-${date.getDate()}`
|
||||
}
|
||||
|
||||
const formatCount = (value: any) => {
|
||||
const numericValue = Number(value)
|
||||
if (!Number.isFinite(numericValue) || numericValue <= 0) return '0'
|
||||
|
||||
const count = Math.floor(numericValue)
|
||||
const compact = (divisor: number, unit: string) => {
|
||||
const formatted = (count / divisor).toFixed(1).replace(/\.0$/, '')
|
||||
return `${formatted}${unit}`
|
||||
}
|
||||
|
||||
if (count >= 100000000) return compact(100000000, '亿')
|
||||
if (count >= 10000) return compact(10000, '万')
|
||||
if (count >= 1000) return compact(1000, 'k')
|
||||
return String(count)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -146,79 +146,83 @@
|
||||
@tap="handleDeletePost">删除帖子</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-interaction">
|
||||
<view class="detail-interaction__header">
|
||||
<text class="detail-interaction__title">互动</text>
|
||||
<view class="detail-interaction__stats">
|
||||
<view class="detail-interaction__stat" @tap="handleLike">
|
||||
<u-icon name="thumb-up" size="34" :color="post.is_liked ? '#1468f5' : '#737373'" />
|
||||
<text :class="{ 'is-active': post.is_liked }">{{ Number(post.like_count || 0) }}</text>
|
||||
</view>
|
||||
<view class="detail-interaction__stat">
|
||||
<u-icon name="chat" size="34" color="#737373" />
|
||||
<text>{{ Number(post.comment_count || 0) }}</text>
|
||||
</view>
|
||||
<view class="detail-interaction__stat">
|
||||
<u-icon name="eye" size="34" color="#737373" />
|
||||
<text>{{ Number(post.view_count || 0) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="detail-comments">
|
||||
<view class="detail-comments__title">
|
||||
<text>评论 ({{ commentTotal }})</text>
|
||||
</view>
|
||||
|
||||
<view v-if="commentError" class="detail-comments__error">
|
||||
<text>评论加载失败</text>
|
||||
<text class="detail-comments__retry" @tap="fetchComments">重新加载</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="comments.length === 0 && !commentLoading" class="detail-comments__empty">
|
||||
<text>暂无评论,快来抢沙发</text>
|
||||
</view>
|
||||
|
||||
<view v-for="comment in comments" :key="comment.id" class="comment-item">
|
||||
<image class="comment-item__avatar" :src="getCommentAvatar(comment)" mode="aspectFill"
|
||||
@error="handleCommentAvatarError(comment)" />
|
||||
<view class="comment-item__body">
|
||||
<view class="comment-item__heading">
|
||||
<text class="comment-item__name">{{ comment.user?.nickname || '匿名' }}</text>
|
||||
<text v-if="isOwnComment(comment)" class="comment-item__more"
|
||||
@tap.stop="handleDeleteComment(comment)">⋮</text>
|
||||
</view>
|
||||
<text class="comment-item__text">{{ comment.content }}</text>
|
||||
<view class="comment-item__meta">
|
||||
<text class="comment-item__time">{{ formatCommentTime(comment.create_time) }}</text>
|
||||
<view class="comment-item__like" @tap.stop="handleCommentLike(comment)">
|
||||
<u-icon name="thumb-up" size="25"
|
||||
:color="comment.is_liked ? '#1468f5' : '#8a8f98'" />
|
||||
<text :class="{ 'is-active': comment.is_liked }">{{ comment.like_count || '' }}</text>
|
||||
<view class="detail-discussion">
|
||||
<view class="detail-interaction">
|
||||
<view class="detail-interaction__header">
|
||||
<text class="detail-interaction__title">互动</text>
|
||||
<view class="detail-interaction__stats">
|
||||
<view class="detail-interaction__stat" @tap="handleLike">
|
||||
<u-icon name="thumb-up" size="34" :color="post.is_liked ? '#1468f5' : '#737373'" />
|
||||
<text :class="{ 'is-active': post.is_liked }">{{ formatCount(post.like_count) }}</text>
|
||||
</view>
|
||||
<text class="comment-item__reply-btn" @tap.stop="openReply(comment)">回复</text>
|
||||
</view>
|
||||
|
||||
<view v-if="comment.replies && comment.replies.length > 0" class="comment-replies">
|
||||
<view v-for="reply in comment.replies" :key="reply.id" class="comment-reply">
|
||||
<text class="comment-reply__name">{{ reply.user?.nickname || '匿名' }}</text>
|
||||
<text v-if="reply.reply_user" class="comment-reply__arrow"> 回复 </text>
|
||||
<text v-if="reply.reply_user" class="comment-reply__name">{{ reply.reply_user.nickname
|
||||
}}</text>
|
||||
<text class="comment-reply__text">:{{ reply.content }}</text>
|
||||
<text v-if="isOwnComment(reply)" class="comment-reply__delete"
|
||||
@tap.stop="handleDeleteComment(reply)"> 删除</text>
|
||||
<view class="detail-interaction__stat">
|
||||
<u-icon name="chat" size="34" color="#737373" />
|
||||
<text>{{ formatCount(post.comment_count) }}</text>
|
||||
</view>
|
||||
<view class="detail-interaction__stat">
|
||||
<u-icon name="eye" size="34" color="#737373" />
|
||||
<text>{{ formatCount(post.view_count) }}</text>
|
||||
</view>
|
||||
<text v-if="comment.reply_count > comment.replies.length" class="comment-replies__more">
|
||||
还有{{ comment.reply_count - comment.replies.length }}条回复
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="commentLoading" class="detail-comments__loading">
|
||||
<u-loading mode="circle" size="28" />
|
||||
<view class="detail-comments">
|
||||
<view v-if="commentError" class="detail-comments__error">
|
||||
<text>评论加载失败</text>
|
||||
<text class="detail-comments__retry" @tap="fetchComments">重新加载</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="comments.length === 0 && !commentLoading" class="detail-comments__empty">
|
||||
<text>暂无评论,快来抢沙发</text>
|
||||
</view>
|
||||
|
||||
<view v-for="comment in comments" :key="comment.id" class="comment-item">
|
||||
<image class="comment-item__avatar" :src="getCommentAvatar(comment)" mode="aspectFill"
|
||||
@error="handleCommentAvatarError(comment)" />
|
||||
<view class="comment-item__body">
|
||||
<view class="comment-item__heading">
|
||||
<text class="comment-item__name">{{ comment.user?.nickname || '匿名' }}</text>
|
||||
<text v-if="isOwnComment(comment)" class="comment-item__more"
|
||||
@tap.stop="handleDeleteComment(comment)">⋮</text>
|
||||
</view>
|
||||
<view class="comment-item__content">
|
||||
<text class="comment-item__text">{{ comment.content }}</text>
|
||||
<view class="comment-item__like" @tap.stop="handleCommentLike(comment)">
|
||||
<u-icon name="thumb-up" size="27"
|
||||
:color="comment.is_liked ? '#1468f5' : '#8a8f98'" />
|
||||
<text v-if="Number(comment.like_count) > 0"
|
||||
:class="{ 'is-active': comment.is_liked }">
|
||||
{{ formatCount(comment.like_count) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="comment-item__meta">
|
||||
<text class="comment-item__time">{{ formatCommentTime(comment.create_time) }}</text>
|
||||
<text class="comment-item__reply-btn" @tap.stop="openReply(comment)">回复</text>
|
||||
</view>
|
||||
|
||||
<view v-if="comment.replies && comment.replies.length > 0" class="comment-replies">
|
||||
<view v-for="reply in comment.replies" :key="reply.id" class="comment-reply">
|
||||
<text class="comment-reply__name">{{ reply.user?.nickname || '匿名' }}</text>
|
||||
<text v-if="reply.reply_user" class="comment-reply__arrow"> 回复 </text>
|
||||
<text v-if="reply.reply_user" class="comment-reply__name">{{ reply.reply_user.nickname
|
||||
}}</text>
|
||||
<text class="comment-reply__text">:{{ reply.content }}</text>
|
||||
<text v-if="isOwnComment(reply)" class="comment-reply__delete"
|
||||
@tap.stop="handleDeleteComment(reply)"> 删除</text>
|
||||
</view>
|
||||
<text v-if="comment.reply_count > comment.replies.length" class="comment-replies__more">
|
||||
还有{{ comment.reply_count - comment.replies.length }}条回复
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="commentLoading" class="detail-comments__loading">
|
||||
<u-loading mode="circle" size="28" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -258,6 +262,7 @@ import { openChat } from '@/api/chat'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { AgreementEnum } from '@/enums/agreementEnums'
|
||||
import { formatCount } from '@/utils/number'
|
||||
import { getToken } from '@/utils/auth'
|
||||
|
||||
const LIUHE_TAGS = ['旧澳六合', '新澳六合']
|
||||
@@ -1419,12 +1424,17 @@ const formatCommentTime = (value: any) => {
|
||||
}
|
||||
}
|
||||
|
||||
.detail-interaction {
|
||||
.detail-discussion {
|
||||
margin-top: 20rpx;
|
||||
padding: 25rpx 24rpx;
|
||||
border: 1rpx solid #e8ebf0;
|
||||
border-radius: 20rpx;
|
||||
background: #ffffff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.detail-interaction {
|
||||
padding: 26rpx 24rpx;
|
||||
border-bottom: 1rpx solid #edf0f4;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
@@ -1435,14 +1445,14 @@ const formatCommentTime = (value: any) => {
|
||||
|
||||
&__title {
|
||||
color: #111111;
|
||||
font-size: 31rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: 720;
|
||||
}
|
||||
|
||||
&__stats {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 36rpx;
|
||||
gap: 40rpx;
|
||||
}
|
||||
|
||||
&__stat {
|
||||
@@ -1450,7 +1460,7 @@ const formatCommentTime = (value: any) => {
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
color: #737373;
|
||||
font-size: 25rpx;
|
||||
font-size: 26rpx;
|
||||
|
||||
.is-active {
|
||||
color: #1468f5;
|
||||
@@ -1459,19 +1469,7 @@ const formatCommentTime = (value: any) => {
|
||||
}
|
||||
|
||||
.detail-comments {
|
||||
margin-top: 16rpx;
|
||||
padding: 26rpx 24rpx 34rpx;
|
||||
border: 1rpx solid #e8ebf0;
|
||||
border-radius: 20rpx;
|
||||
background: #ffffff;
|
||||
|
||||
&__title {
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #edf0f4;
|
||||
color: #191b20;
|
||||
font-size: 31rpx;
|
||||
font-weight: 720;
|
||||
}
|
||||
padding: 0 24rpx 12rpx;
|
||||
|
||||
&__empty,
|
||||
&__error {
|
||||
@@ -1496,9 +1494,9 @@ const formatCommentTime = (value: any) => {
|
||||
}
|
||||
|
||||
.comment-item {
|
||||
padding: 26rpx 0;
|
||||
padding: 30rpx 0;
|
||||
display: flex;
|
||||
gap: 18rpx;
|
||||
gap: 20rpx;
|
||||
border-bottom: 1rpx solid #edf0f4;
|
||||
|
||||
&:last-of-type {
|
||||
@@ -1506,8 +1504,8 @@ const formatCommentTime = (value: any) => {
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
width: 68rpx;
|
||||
height: 68rpx;
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
background: #edf2f8;
|
||||
@@ -1526,8 +1524,8 @@ const formatCommentTime = (value: any) => {
|
||||
}
|
||||
|
||||
&__name {
|
||||
color: #22252b;
|
||||
font-size: 27rpx;
|
||||
color: #17191e;
|
||||
font-size: 28rpx;
|
||||
font-weight: 680;
|
||||
}
|
||||
|
||||
@@ -1541,20 +1539,27 @@ const formatCommentTime = (value: any) => {
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
&__content {
|
||||
margin-top: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18rpx;
|
||||
}
|
||||
|
||||
&__text {
|
||||
display: block;
|
||||
margin-top: 9rpx;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
color: #2f333a;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.65;
|
||||
font-size: 29rpx;
|
||||
line-height: 1.62;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
&__meta {
|
||||
margin-top: 12rpx;
|
||||
margin-top: 14rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 28rpx;
|
||||
gap: 30rpx;
|
||||
}
|
||||
|
||||
&__time,
|
||||
@@ -1565,9 +1570,12 @@ const formatCommentTime = (value: any) => {
|
||||
}
|
||||
|
||||
&__like {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6rpx;
|
||||
gap: 8rpx;
|
||||
color: #737a86;
|
||||
font-size: 24rpx;
|
||||
|
||||
.is-active {
|
||||
color: #1468f5;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 将互动等非负整数转换为紧凑展示格式。
|
||||
*/
|
||||
export const formatCount = (value: unknown): string => {
|
||||
const numericValue = Number(value)
|
||||
if (!Number.isFinite(numericValue) || numericValue <= 0) return '0'
|
||||
|
||||
const count = Math.floor(numericValue)
|
||||
const compact = (divisor: number, unit: string) => {
|
||||
const formatted = (count / divisor).toFixed(1).replace(/\.0$/, '')
|
||||
return `${formatted}${unit}`
|
||||
}
|
||||
|
||||
if (count >= 100000000) return compact(100000000, '亿')
|
||||
if (count >= 10000) return compact(10000, '万')
|
||||
if (count >= 1000) return compact(1000, 'k')
|
||||
return String(count)
|
||||
}
|
||||
Reference in New Issue
Block a user