This commit is contained in:
2026-06-13 11:02:47 +08:00
parent 318ddd85a5
commit 68678304ff
15 changed files with 659 additions and 78 deletions
+11 -2
View File
@@ -2,11 +2,12 @@
<div class="moment-card">
<!-- 头部头像 + 用户名 + 时间 + 删除 -->
<div class="moment-header">
<n-avatar :size="40" round :style="{ background: 'var(--color-primary)' }">
<n-avatar :size="40" round :style="{ background: 'var(--color-primary)', cursor: isMine ? 'default' : 'pointer' }"
@click="goToUserMoments">
{{ (moment.nickname || moment.username || '?')[0] }}
</n-avatar>
<div class="header-info">
<span class="author-name">{{ moment.nickname || moment.username }}</span>
<span class="author-name" :style="{ cursor: isMine ? 'default' : 'pointer' }" @click="goToUserMoments">{{ moment.nickname || moment.username }}</span>
<span class="post-time">{{ formatTime(moment.created_at) }}</span>
</div>
<span v-if="isMine" class="delete-btn" title="删除" @click="handleDelete">🗑</span>
@@ -66,6 +67,7 @@
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { useRouter } from 'vue-router'
import { momentsApi } from '@/api/moments'
import { useAuthStore } from '@/stores/auth'
import dayjs from 'dayjs'
@@ -83,6 +85,7 @@ const emit = defineEmits<{
}>()
const auth = useAuthStore()
const router = useRouter()
const showComments = ref(false)
const comments = ref<any[]>([])
const commentText = ref('')
@@ -90,6 +93,12 @@ const replyTarget = ref<any>(null)
const isMine = computed(() => props.moment.user_id === auth.user?.id)
function goToUserMoments() {
if (props.moment.user_id && !isMine.value) {
router.push(`/moments/user/${props.moment.user_id}`)
}
}
watch(showComments, async (val) => {
if (val && comments.value.length === 0) {
try {
@@ -0,0 +1,117 @@
<template>
<div class="user-moments">
<div class="panel-header">
<n-button quaternary circle size="small" @click="$router.push('/moments')"></n-button>
<h3 class="panel-title">{{ userName }} 的朋友圈</h3>
</div>
<div class="moments-content">
<div v-if="isLoading" class="loading">加载中...</div>
<div v-else-if="moments.length === 0" class="empty">
<div style="font-size: 48px">🌿</div>
<p style="color: var(--color-text-secondary)">还没有动态</p>
</div>
<div v-else class="moment-list">
<MomentCard
v-for="moment in moments"
:key="moment.id"
:moment="moment"
@toggle-like="handleToggleLike"
@comment="handleComment"
@delete="handleDelete"
/>
</div>
<div v-if="hasMore && moments.length > 0" class="load-more">
<n-button text size="small" @click="loadMore">加载更多</n-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { useMessage } from 'naive-ui'
import { momentsApi } from '@/api/moments'
import MomentCard from './MomentCard.vue'
const route = useRoute()
const message = useMessage()
const moments = ref<any[]>([])
const isLoading = ref(false)
const hasMore = ref(true)
const cursor = ref<string | null>(null)
const userName = ref('用户')
onMounted(() => {
const userId = route.params.userId as string
if (userId) fetchUserMoments(userId, true)
})
async function fetchUserMoments(userId: string, refresh = false) {
if (isLoading.value) return
isLoading.value = true
try {
const cur = refresh ? undefined : cursor.value || undefined
const { data } = await momentsApi.getUserMoments(userId, cur)
if (refresh) {
moments.value = data
} else {
moments.value = [...moments.value, ...data]
}
if (data.length > 0) {
cursor.value = data[data.length - 1].id
if (!userName.value || userName.value === '用户') {
userName.value = data[0].nickname || data[0].username || '用户'
}
}
if (data.length < 20) hasMore.value = false
} catch {
message.error('加载失败')
} finally {
isLoading.value = false
}
}
function loadMore() {
const userId = route.params.userId as string
fetchUserMoments(userId)
}
async function handleToggleLike(momentId: string) {
try {
const { data } = await momentsApi.toggleLike(momentId)
const m = moments.value.find((m) => m.id === momentId)
if (m) {
m.is_liked = data.is_liked
m.like_count = data.is_liked ? m.like_count + 1 : Math.max(0, m.like_count - 1)
}
} catch { message.error('操作失败') }
}
async function handleComment(momentId: string, content: string) {
message.success('评论成功')
}
async function handleDelete(momentId: string) {
try {
await momentsApi.deleteMoment(momentId)
moments.value = moments.value.filter((m) => m.id !== momentId)
message.success('已删除')
} catch { message.error('删除失败') }
}
</script>
<style scoped>
.user-moments { display: flex; flex-direction: column; height: 100%; }
.panel-header {
display: flex; align-items: center; gap: 8px;
padding: 16px; border-bottom: 1px solid var(--color-border);
}
.panel-title { margin: 0; font-size: 16px; font-weight: 600; }
.moments-content { flex: 1; overflow-y: auto; padding: 12px; }
.loading { text-align: center; padding: 40px; color: var(--color-text-hint); }
.empty { text-align: center; padding: 60px 20px; }
.moment-list { display: flex; flex-direction: column; gap: 12px; }
.load-more { text-align: center; padding: 12px; }
</style>