mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-07-27 04:20:43 +08:00
feat: 新增点赞、游客评论、分享等核心功能,重构页面逻辑
1. 新增全局点赞状态管理,使用metadata.name做key并持久化存储 2. 新增游客信息存储与评论面板,支持游客评论互动 3. 新增全局分享hook,为多个页面配置分享功能 4. 重构登录页面,支持登录/注册切换与游客访问模式 5. 合并注册页到登录页,简化路由结构 6. 优化剪贴板工具函数,支持自定义提示文本 7. 重构个人中心页面,支持游客信息管理 8. 修复相册页面标题与样式问题 9. 移除无用的备份文件与测试代码
This commit is contained in:
@@ -2,8 +2,11 @@
|
||||
import { createComment, createReply, listCommentReplies, listComments } from '@/api/halo-base/comment'
|
||||
import { avatar } from '@/utils/imageHelper'
|
||||
import { timeFrom } from '@/utils/base/timeFrom'
|
||||
import { useGuestStore } from '@/store/guest'
|
||||
import { useUpvoteStore } from '@/store/upvote'
|
||||
import type { CommentV1alpha1PublicApiListCommentRepliesRequest, CommentV1alpha1PublicApiListComments1Request, CommentWithReplyVo, ReplyVo } from '@halo-dev/api-client'
|
||||
import type { IHaloListResponseBase } from '@/api/types/halo'
|
||||
import UhGuestInfoPanel from './uh-guest-info-panel.vue'
|
||||
|
||||
interface IProps {
|
||||
postName: string
|
||||
@@ -20,6 +23,9 @@ const emits = defineEmits<{
|
||||
(e: 'commented'): void
|
||||
}>()
|
||||
|
||||
const guestStore = useGuestStore()
|
||||
const upvoteStore = useUpvoteStore()
|
||||
|
||||
// --- 评论列表 ---
|
||||
const commentList = ref<CommentWithReplyVo[]>([])
|
||||
const commentPage = ref(1)
|
||||
@@ -44,6 +50,26 @@ const replyLoadingMap = ref<Record<string, boolean>>({})
|
||||
const replyHasNextMap = ref<Record<string, boolean>>({})
|
||||
const replyPageMap = ref<Record<string, number>>({})
|
||||
|
||||
// --- 游客信息面板 ---
|
||||
const showGuestPanel = ref(false)
|
||||
|
||||
// --- 点赞(切换) ---
|
||||
async function handleUpvote(name: string) {
|
||||
const nowUpvoted = await upvoteStore.toggle(name, 'comments')
|
||||
// 更新本地计数
|
||||
for (const comment of commentList.value) {
|
||||
if (comment.metadata.name === name) {
|
||||
comment.stats.upvote = Math.max(0, (comment.stats.upvote ?? 0) + (nowUpvoted ? 1 : -1))
|
||||
break
|
||||
}
|
||||
const reply = comment.replies?.items?.find(r => r.metadata.name === name)
|
||||
if (reply) {
|
||||
reply.stats.upvote = Math.max(0, (reply.stats.upvote ?? 0) + (nowUpvoted ? 1 : -1))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 获取评论列表 ---
|
||||
async function fetchComments(page = 1, append = false) {
|
||||
commentLoading.value = true
|
||||
@@ -152,13 +178,29 @@ function handleCancelReply() {
|
||||
}
|
||||
|
||||
// --- 提交评论/回复 ---
|
||||
async function handleSubmit() {
|
||||
function handleSubmit() {
|
||||
const text = inputText.value.trim()
|
||||
if (!text || submitLoading.value)
|
||||
return
|
||||
|
||||
// 检查游客信息,未设置直接弹出面板
|
||||
if (!guestStore.isComplete) {
|
||||
showGuestPanel.value = true
|
||||
return
|
||||
}
|
||||
|
||||
doSubmit(text)
|
||||
}
|
||||
|
||||
async function doSubmit(text: string) {
|
||||
submitLoading.value = true
|
||||
try {
|
||||
const owner = {
|
||||
displayName: guestStore.info.displayName,
|
||||
email: guestStore.info.email,
|
||||
website: guestStore.info.website || undefined,
|
||||
}
|
||||
|
||||
if (replyTarget.value) {
|
||||
await createReply({
|
||||
name: replyTarget.value.commentName,
|
||||
@@ -167,6 +209,7 @@ async function handleSubmit() {
|
||||
raw: text,
|
||||
quoteReply: replyTarget.value.replyName || undefined,
|
||||
allowNotification: true,
|
||||
owner,
|
||||
},
|
||||
} as any)
|
||||
}
|
||||
@@ -182,6 +225,8 @@ async function handleSubmit() {
|
||||
version: props.commentSubjectVersion,
|
||||
},
|
||||
allowNotification: true,
|
||||
hidden: false,
|
||||
owner,
|
||||
},
|
||||
} as any)
|
||||
}
|
||||
@@ -201,6 +246,16 @@ async function handleSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
// --- 游客信息保存后自动提交 ---
|
||||
function handleGuestSaved() {
|
||||
showGuestPanel.value = false
|
||||
// 保存后如果有输入内容,自动提交
|
||||
const text = inputText.value.trim()
|
||||
if (text) {
|
||||
doSubmit(text)
|
||||
}
|
||||
}
|
||||
|
||||
// --- 查找被引用的回复 ---
|
||||
function findQuotedReply(comment: CommentWithReplyVo, quoteReplyName: string): ReplyVo | undefined {
|
||||
return comment.replies?.items?.find(r => r.metadata.name === quoteReplyName)
|
||||
@@ -291,6 +346,24 @@ onMounted(() => {
|
||||
|
||||
<!-- 操作栏 -->
|
||||
<view class="flex items-center gap-x-4">
|
||||
<!-- 点赞 -->
|
||||
<view
|
||||
class="flex items-center gap-x-0.5"
|
||||
@click="handleUpvote(comment.metadata.name)"
|
||||
>
|
||||
<wd-icon
|
||||
name="thumb-up"
|
||||
:color="upvoteStore.isUpvoted(comment.metadata.name) ? '#EF4444' : '#9CA3AF'"
|
||||
:size="14"
|
||||
/>
|
||||
<text
|
||||
class="text-[11px]"
|
||||
:class="upvoteStore.isUpvoted(comment.metadata.name) ? 'text-red-500' : 'text-gray-400'"
|
||||
>
|
||||
{{ comment.stats.upvote || '' }}
|
||||
</text>
|
||||
</view>
|
||||
<!-- 回复 -->
|
||||
<view
|
||||
class="flex items-center gap-x-1"
|
||||
@click="handleReply(comment.metadata.name, undefined, comment.owner.displayName || '匿名用户', comment.spec.raw)"
|
||||
@@ -298,6 +371,7 @@ onMounted(() => {
|
||||
<wd-icon name="message" color="#9CA3AF" :size="14" />
|
||||
<text class="text-[11px] text-gray-400">回复</text>
|
||||
</view>
|
||||
<!-- 展开回复 -->
|
||||
<view
|
||||
v-if="(comment.status?.replyCount ?? 0) > 0"
|
||||
class="flex items-center gap-x-1"
|
||||
@@ -345,12 +419,33 @@ onMounted(() => {
|
||||
<rich-text :nodes="reply.spec.raw" />
|
||||
</text>
|
||||
|
||||
<view
|
||||
class="flex items-center gap-x-1"
|
||||
@click="handleReply(comment.metadata.name, reply.metadata.name, reply.owner.displayName || '匿名用户', reply.spec.raw)"
|
||||
>
|
||||
<wd-icon name="message" color="#9CA3AF" :size="12" />
|
||||
<text class="text-[10px] text-gray-400">回复</text>
|
||||
<!-- 回复操作栏 -->
|
||||
<view class="flex items-center gap-x-3">
|
||||
<!-- 点赞 -->
|
||||
<view
|
||||
class="flex items-center gap-x-0.5"
|
||||
@click="handleUpvote(reply.metadata.name)"
|
||||
>
|
||||
<wd-icon
|
||||
name="thumb-up"
|
||||
:color="upvoteStore.isUpvoted(reply.metadata.name) ? '#EF4444' : '#9CA3AF'"
|
||||
:size="12"
|
||||
/>
|
||||
<text
|
||||
class="text-[10px]"
|
||||
:class="upvoteStore.isUpvoted(reply.metadata.name) ? 'text-red-500' : 'text-gray-400'"
|
||||
>
|
||||
{{ reply.stats.upvote || '' }}
|
||||
</text>
|
||||
</view>
|
||||
<!-- 回复 -->
|
||||
<view
|
||||
class="flex items-center gap-x-1"
|
||||
@click="handleReply(comment.metadata.name, reply.metadata.name, reply.owner.displayName || '匿名用户', reply.spec.raw)"
|
||||
>
|
||||
<wd-icon name="message" color="#9CA3AF" :size="12" />
|
||||
<text class="text-[10px] text-gray-400">回复</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -422,4 +517,11 @@ onMounted(() => {
|
||||
</view>
|
||||
</wd-transition>
|
||||
</view>
|
||||
|
||||
<!-- 游客信息面板 -->
|
||||
<uh-guest-info-panel
|
||||
v-if="showGuestPanel"
|
||||
@close="showGuestPanel = false"
|
||||
@saved="handleGuestSaved"
|
||||
/>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user