1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-07-27 04:20:43 +08:00

style: 调整背景模糊值与布局样式,优化评论与图片功能

1.  调整全局背景模糊参数,统一并优化页面毛玻璃效果
2.  调整home页布局类名顺序,为post-card添加全屏宽度
3.  新增头像处理工具函数,补充随机图片源
4.  优化markdown内容容器的内边距样式
5.  重构文章详情页评论面板,实现完整评论回复功能
6.  优化文章详情页布局与样式细节
This commit is contained in:
小莫唐尼
2026-06-15 21:29:14 +08:00
parent a86baa9641
commit e191eb8358
7 changed files with 480 additions and 122 deletions
@@ -1,84 +1,400 @@
<script setup lang="ts">
import { listComments } from '@/api/halo-base/comment'
import { createComment, createReply, listCommentReplies, listComments } from '@/api/halo-base/comment'
import { completeUrl } from '@/utils/url'
import { useHaloScroll } from '@/hooks/useHaloScroll'
import type { CommentV1alpha1PublicApiListComments1Request, CommentWithReplyVo, PostVo } from '@halo-dev/api-client'
import { avatar } from '@/utils/imageHelper'
import { timeFrom } from '@/utils/base/timeFrom'
import type { CommentV1alpha1PublicApiListCommentRepliesRequest, CommentV1alpha1PublicApiListComments1Request, CommentWithReplyVo, ReplyVo } from '@halo-dev/api-client'
import type { IHaloListResponseBase } from '@/api/types/halo'
interface IProps {
post: PostVo | undefined
postName: string
commentSubjectVersion: string
commentCount?: number
}
const props = defineProps<IProps>()
const props = withDefaults(defineProps<IProps>(), {
commentCount: 0,
})
const emits = defineEmits<{
(e: 'close'): void
(e: 'commented'): void
}>()
const {
response, // 响应式的原始数据响应
list, // 响应式的数据列表
loading, // 是否加载中
finished, // 是否已全部加载
error, // 是否加载失败
refresh, // 刷新数据的函数
loadMore, // 加载更多数据的函数
} = useHaloScroll<CommentWithReplyVo, CommentV1alpha1PublicApiListComments1Request>({
fetchData: listComments,
params: {
version: 'v1alpha1',
kind: 'Comment',
page: 1,
size: 12,
name: props.post?.metadata?.name ?? undefined,
},
// --- 评论列表 ---
const commentList = ref<CommentWithReplyVo[]>([])
const commentPage = ref(1)
const commentHasNext = ref(false)
const commentLoading = ref(false)
// --- 回复目标 ---
const replyTarget = ref<{
commentName: string
replyName?: string
displayName: string
} | null>(null)
// --- 输入内容 ---
const inputText = ref('')
const submitLoading = ref(false)
// --- 展开回复的评论 ---
const expandedReplies = ref<Set<string>>(new Set())
const replyLoadingMap = ref<Record<string, boolean>>({})
const replyHasNextMap = ref<Record<string, boolean>>({})
const replyPageMap = ref<Record<string, number>>({})
// --- 获取评论列表 ---
async function fetchComments(page = 1, append = false) {
commentLoading.value = true
try {
const res = await listComments({
group: 'content.halo.run',
name: props.postName,
kind: 'Post',
version: props.commentSubjectVersion,
page,
size: 20,
sort: ['metadata.creationTimestamp,desc'],
} as CommentV1alpha1PublicApiListComments1Request)
const data = res as unknown as IHaloListResponseBase<CommentWithReplyVo>
if (append) {
commentList.value = [...commentList.value, ...(data.items ?? [])]
}
else {
commentList.value = data.items ?? []
}
commentHasNext.value = data.hasNext
commentPage.value = page
}
catch (err) {
console.error('获取评论失败:', err)
}
finally {
commentLoading.value = false
}
}
// --- 加载更多评论 ---
function handleLoadMoreComments() {
if (commentLoading.value || !commentHasNext.value)
return
fetchComments(commentPage.value + 1, true)
}
// --- 获取回复列表 ---
async function fetchReplies(commentName: string, page = 1, append = false) {
replyLoadingMap.value[commentName] = true
try {
const res = await listCommentReplies({
name: commentName,
page,
size: 10,
} as CommentV1alpha1PublicApiListCommentRepliesRequest)
const data = res as unknown as IHaloListResponseBase<ReplyVo>
const comment = commentList.value.find(c => c.metadata.name === commentName)
if (comment) {
if (append) {
comment.replies = {
...comment.replies,
items: [...(comment.replies?.items ?? []), ...(data.items ?? [])],
hasNext: data.hasNext,
} as any
}
else {
comment.replies = {
...data,
items: data.items ?? [],
} as any
}
}
replyHasNextMap.value[commentName] = data.hasNext
replyPageMap.value[commentName] = page
}
catch (err) {
console.error('获取回复失败:', err)
uni.showToast({ title: '获取回复失败', icon: 'none' })
}
finally {
replyLoadingMap.value[commentName] = false
}
}
// --- 切换展开回复 ---
function handleToggleReplies(comment: CommentWithReplyVo) {
const name = comment.metadata.name
if (expandedReplies.value.has(name)) {
expandedReplies.value.delete(name)
}
else {
expandedReplies.value.add(name)
if (!comment.replies?.items?.length) {
fetchReplies(name)
}
}
}
// --- 加载更多回复 ---
function handleLoadMoreReplies(commentName: string) {
if (replyLoadingMap.value[commentName] || !replyHasNextMap.value[commentName])
return
fetchReplies(commentName, (replyPageMap.value[commentName] ?? 1) + 1, true)
}
// --- 设置回复目标 ---
function handleReply(commentName: string, replyName: string | undefined, displayName: string) {
replyTarget.value = { commentName, replyName, displayName }
}
// --- 取消回复 ---
function handleCancelReply() {
replyTarget.value = null
}
// --- 提交评论/回复 ---
async function handleSubmit() {
const text = inputText.value.trim()
if (!text || submitLoading.value)
return
submitLoading.value = true
try {
if (replyTarget.value) {
await createReply({
name: replyTarget.value.commentName,
replyRequest: {
content: text,
raw: text,
quoteReply: replyTarget.value.replyName || undefined,
allowNotification: true,
},
} as any)
}
else {
await createComment({
commentRequest: {
content: text,
raw: text,
subjectRef: {
group: 'content.halo.run',
kind: 'Post',
name: props.postName,
version: props.commentSubjectVersion,
},
allowNotification: true,
},
} as any)
}
inputText.value = ''
replyTarget.value = null
uni.showToast({ title: '评论成功', icon: 'none' })
await fetchComments(1)
emits('commented')
}
catch (err) {
console.error('评论失败:', err)
uni.showToast({ title: '评论失败', icon: 'none' })
}
finally {
submitLoading.value = false
}
}
// --- 关闭面板 ---
function handleClose() {
emits('close')
}
defineExpose({
refresh: () => fetchComments(),
})
onMounted(() => {
fetchComments()
})
</script>
<template>
<!-- 评论面板根据分组显示 -->
<view
class="uh-backdrop-blur-xs fixed inset-0 z-110 flex items-end justify-center bg-black/20"
@click.stop="emits('close')"
>
<wd-transition :show="true" :lazy-render="true" :duration="150" name="fade-up">
<view class="box-border w-screen rounded-2xl bg-white p-4" @click.stop>
<view class="box-border w-full flex shrink-0 items-center justify-between gap-x-4">
<view class="text-gray-900 font-bold">
记录点什么
</view>
<view class="shrink-0">
<wd-icon name="close-circle" size="24px" color="#1A1A1A" @click="emits('close')" />
<view class="uh-backdrop-blur fixed inset-0 z-110 flex items-end bg-black/20" @click="handleClose">
<wd-transition :show="true" :duration="300" name="slide-up" :lazy-render="false">
<!-- 面板主体 -->
<view
class="box-border w-screen flex flex-col rounded-t-3xl bg-white"
@click.stop
>
<!-- 拖拽指示条 -->
<view class="w-full center py-2" @click.stop="handleClose">
<view class="h-1 w-10 rounded-full bg-gray-300" />
</view>
<!-- 头部 -->
<view class="box-border flex items-center justify-between border-b border-gray-100 px-4 pb-3">
<text class="text-base text-gray-900 font-bold">
说点什么 <text v-if="commentCount > 0" class="text-sm text-gray-600 font-normal">{{ commentCount }}</text>
</text>
<view class="h-8 w-8 center rounded-full bg-gray-50" @click="handleClose">
<wd-icon name="close" color="#6B7280" :size="18" />
</view>
</view>
<view class="mt-2 w-full">
<scroll-view
v-if="list.length > 0" class="h-[60vh] w-full" scroll-y :refresher-enabled="true"
:refresher-triggered="loading" @scrolltolower="loadMore" @refresherrefresh="refresh"
>
<view class="box-border w-full">
这里是评论记录
<!-- 评论列表 -->
<scroll-view
class="h-[60vh] overflow-hidden"
scroll-y
@scrolltolower="handleLoadMoreComments"
>
<view class="box-border h-full px-4 pt-3">
<!-- 加载中 -->
<view v-if="commentLoading && commentList.length === 0" class="box-border h-full center">
<wd-loading />
</view>
<!-- 空状态 -->
<view v-else-if="!commentLoading && commentList.length === 0" class="box-border h-full center flex-col gap-y-2">
<wd-icon name="chat" color="#D1D5DB" :size="40" />
<text class="text-sm text-gray-400">快来抢沙发吧~</text>
</view>
<!-- 评论列表 -->
<view v-else class="box-border h-full w-full flex flex-col">
<view
v-for="comment in commentList"
:key="comment.metadata.name"
class="border-b border-gray-50 py-3"
>
<view class="flex gap-x-3">
<!-- 头像 -->
<image
class="uh-shadow-md box-border h-9 w-9 shrink-0 border-2 border-white rounded-xl border-solid bg-gray-100"
:src="avatar(comment.owner.avatar)"
mode="aspectFill"
/>
<!-- 内容区 -->
<view class="flex flex-1 flex-col gap-y-2 overflow-hidden">
<view class="flex items-center justify-between">
<text class="text-xs text-gray-500 font-medium">{{ comment.owner.displayName || '匿名用户' }}</text>
<text class="text-[11px] text-gray-400">{{ timeFrom(comment.spec.creationTime || comment.metadata.creationTimestamp) }}</text>
</view>
<text class="text-sm text-gray-900 leading-5">
<rich-text :nodes="comment.spec.raw" />
</text>
<!-- 操作栏 -->
<view class="flex items-center gap-x-4">
<view
class="flex items-center gap-x-1"
@click="handleReply(comment.metadata.name, undefined, comment.owner.displayName || '匿名用户')"
>
<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"
@click="handleToggleReplies(comment)"
>
<wd-icon
:name="expandedReplies.has(comment.metadata.name) ? 'up' : 'down'"
color="#9CA3AF"
:size="14"
/>
<text class="text-[11px] text-gray-400">
{{ expandedReplies.has(comment.metadata.name) ? '收起' : `${comment.status?.replyCount}条回复` }}
</text>
</view>
</view>
<!-- 回复列表 -->
<view v-if="expandedReplies.has(comment.metadata.name)" class="mt-2 flex flex-col gap-y-2 rounded-xl bg-gray-50 p-3">
<view
v-for="reply in (comment.replies?.items ?? [])"
:key="reply.metadata.name"
class="flex gap-x-2.5"
>
<image
class="h-7 w-7 shrink-0 border-2 border-white rounded-lg border-solid bg-gray-200"
:src="avatar(reply.owner.avatar)"
mode="aspectFill"
/>
<view class="flex flex-1 flex-col gap-y-1 overflow-hidden">
<view class="flex items-center justify-between">
<text class="text-[11px] text-gray-500 font-medium">{{ reply.owner.displayName || '匿名用户' }}</text>
<text class="text-[10px] text-gray-400">{{ timeFrom(reply.spec.creationTime || reply.metadata.creationTimestamp) }}</text>
</view>
<view v-if="reply.spec.quoteReply" class="rounded-lg bg-gray-100 px-2.5 py-1.5">
<text class="text-[11px] text-gray-400">回复 {{ reply.spec.quoteReply }}</text>
</view>
<text class="text-xs text-gray-700 leading-4.5">{{ reply.spec.raw }}</text>
<view
class="flex items-center gap-x-1"
@click="handleReply(comment.metadata.name, reply.metadata.name, reply.owner.displayName || '匿名用户')"
>
<wd-icon name="chat" color="#9CA3AF" :size="12" />
<text class="text-[10px] text-gray-400">回复</text>
</view>
</view>
</view>
<!-- 加载更多回复 -->
<view
v-if="replyHasNextMap[comment.metadata.name]"
class="center py-1"
@click="handleLoadMoreReplies(comment.metadata.name)"
>
<text class="text-xs text-gray-400">
{{ replyLoadingMap[comment.metadata.name] ? '加载中...' : '查看更多回复' }}
</text>
</view>
<view v-if="replyLoadingMap[comment.metadata.name] && !(comment.replies?.items?.length)" class="center py-2">
<wd-loading :size="20" />
</view>
</view>
</view>
</view>
</view>
<!-- 加载更多 -->
<view v-if="commentHasNext" class="center py-3" @click="handleLoadMoreComments">
<text class="text-xs text-gray-400">{{ commentLoading ? '加载中...' : '加载更多评论' }}</text>
</view>
<view class="h-4 w-full" />
</view>
</scroll-view>
<view v-else class="h-[60vh] flex items-center justify-center">
<wd-empty tip="暂无数据">
<template #image>
<wd-icon name="empty" color="#1A1A1A" :size="52" />
</template>
</wd-empty>
</view>
<view class="mt-4 w-full flex flex-col items-center justify-center gap-y-2 text-xs text-gray-900">
<view class="text-xs text-gray-900">
{{ response?.total || 0 }} 条记录 已加载 {{ list.length }} 条记录
</scroll-view>
<!-- 底部输入区 -->
<view class="box-border border-t border-gray-100 px-4 pt-2">
<!-- 回复提示 -->
<view v-if="replyTarget" class="mb-2 flex items-center justify-between rounded-lg bg-gray-50 px-3 py-1.5">
<text class="text-xs text-gray-500">回复 {{ replyTarget.displayName }}</text>
<view @click="handleCancelReply">
<wd-icon name="close" color="#9CA3AF" :size="14" />
</view>
<view v-if="loading || error || finished">
<wd-loading v-if="loading" text="加载中..." direction="horizontal" color="#1A1A1A" :size="16" />
<view v-else-if="error" class="w-full rounded-lg bg-gray-900 px-2 py-1.5 text-center text-xs text-white">
<wd-icon name="refresh" :size="14" /> 加载失败点击重试
</view>
<view v-else-if="finished" class="text-xs text-gray-900">
数据已全部加载
</view>
</view>
<view class="flex items-center gap-x-3 pb-2">
<input
v-model="inputText"
class="flex-1 rounded-xl bg-gray-50 px-4 py-2.5 text-sm active:bg-gray-100 focus:bg-gray-100"
:placeholder="replyTarget ? `回复 ${replyTarget.displayName}...` : '写评论...'"
confirm-type="send"
:adjust-position="true"
@confirm="handleSubmit"
>
<view
class="h-9 w-9 center shrink-0 rounded-xl transition-colors"
:class="inputText.trim() ? 'bg-gray-900' : 'bg-gray-200'"
@click="handleSubmit"
>
<wd-icon name="arrow-up" :color="inputText.trim() ? '#FFFFFF' : '#9CA3AF'" :size="18" />
</view>
</view>
</view>
+2 -2
View File
@@ -97,8 +97,8 @@ font-size: 14px;
strong: `font-weight: 700;color: ${primaryColor};`,
video: 'width: 100%',
},
// containStyle: 'font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;color: #606266;word-spacing: 0.8px;letter-spacing: 0.8px; background-color:transparent;',
containStyle: 'word-spacing: 0.8px;letter-spacing: 0.8px; background-color:transparent;',
// containStyle: 'font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;color: #606266;word-spacing: 0.8px;letter-spacing: 0.8px; background-color:transparent;',
containStyle: 'padding:0 12px;word-spacing: 0.8px;letter-spacing: 0.8px; background-color:transparent;',
loadingGif: '',
emptyGif: '',
+2 -2
View File
@@ -127,7 +127,7 @@ onPageScroll(() => { })
</script>
<template>
<view class="bg-page3 min-h-screen">
<view class="min-h-screen bg-page3">
<!-- 顶部banner -->
<view v-if="false" class="relative h-72 w-full">
<image class="h-72 w-full object-cover" :src="exampleImageUrls.banner" />
@@ -237,7 +237,7 @@ onPageScroll(() => { })
<wd-empty :icon-size="60" icon="no-result" tip="暂无数据" />
</view>
<template v-else>
<post-card v-for="post in postList" :key="post.metadata.name" :post="post" :type="postCardStyle" />
<post-card v-for="post in postList" :key="post.metadata.name" :post="post" :type="postCardStyle" class="w-full" />
</template>
</view>
</view>
+3 -3
View File
@@ -11,11 +11,11 @@
}
.uh-backdrop-blur {
backdrop-filter: blur(4rpx);
backdrop-filter: blur(2rpx);
}
.uh-backdrop-blur-xs {
backdrop-filter: blur(8rpx);
backdrop-filter: blur(4rpx);
}
.uh-backdrop-blur-sm {
@@ -23,5 +23,5 @@
}
.uh-backdrop-blur-md {
backdrop-filter: blur(24rpx);
backdrop-filter: blur(16rpx);
}
+48 -51
View File
@@ -10,6 +10,7 @@ import { formatNumberUnit } from '@/utils/base/digital'
import type { NavigationPostVo, PostV1alpha1PublicApiQueryPostByNameRequest, PostVo } from '@halo-dev/api-client'
import type { VoteRequest } from '@/api/halo-base/trackers'
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue'
import UhPostCommentPanel from '@/components/post-detail/uh-post-comment-panel.vue'
interface IPostDetailLoadOptions {
metadataName: string
@@ -154,22 +155,11 @@ onShareAppMessage(() => {
}
})
const commentPanel = reactive<{
show: boolean
post: PostVo | undefined
}>({
show: false,
post: undefined,
})
// --- 评论面板 ---
const showCommentPanel = ref(false)
function handleOpenCommentPanel(post: PostVo) {
commentPanel.show = true
commentPanel.post = post
}
function handleCloseCommentPanel() {
commentPanel.show = false
commentPanel.post = undefined
function handleOpenCommentPanel() {
showCommentPanel.value = true
}
</script>
@@ -177,6 +167,7 @@ function handleCloseCommentPanel() {
<view class="min-h-screen w-full flex flex-col bg-page3">
<!-- 顶部导航栏 -->
<view
v-if="false"
class="fixed left-0 z-100 box-border w-full flex items-center justify-between"
:style="{ paddingTop: 'calc(var(--status-bar-height, 44px) ' }"
>
@@ -203,12 +194,12 @@ function handleCloseCommentPanel() {
<!-- 文章信息卡片覆盖封面底部 -->
<view class="relative z-20 box-border w-full flex flex-col gap-y-4 pt-52 -mt-8">
<!-- 主信息卡片 -->
<view v-if="htmlContent" class="uh-shadow-xs box-border w-full flex flex-col gap-y-3 overflow-hidden rounded-3xl bg-white p-4">
<view v-if="htmlContent" class="uh-shadow-xs box-border w-full flex flex-col gap-y-3 overflow-hidden rounded-3xl bg-white">
<!-- 标题 -->
<text class="text-lg text-gray-900 font-bold leading-6">{{ post?.spec?.title ?? '加载中...' }}</text>
<text class="box-border p-4 pb-0 text-lg text-gray-900 font-bold leading-6">{{ post?.spec?.title ?? '加载中...' }}</text>
<!-- 分类 & 标签 -->
<view v-if="categories.length > 0 || tags.length > 0" class="flex flex-wrap items-center gap-2">
<view v-if="categories.length > 0 || tags.length > 0" class="box-border flex flex-wrap items-center gap-2 px-4">
<view
v-for="cat in categories" :key="cat.metadata.name"
class="rounded-full bg-gray-900 px-2.5 py-1 text-xs text-white font-medium"
@@ -224,7 +215,7 @@ function handleCloseCommentPanel() {
</view>
<!-- 元信息 -->
<view class="flex flex-1 items-center gap-x-1">
<view class="box-border flex flex-1 items-center gap-x-1 px-4">
<text class="text-[11px] text-gray-500">{{ publishTime }}</text>
<text class="text-[11px] text-gray-400">·</text>
<text class="text-[11px] text-gray-500">浏览 {{ stats.visit }}</text>
@@ -237,10 +228,10 @@ function handleCloseCommentPanel() {
</view>
<!-- 分割线 -->
<view class="h-1px w-full bg-gray-50" />
<view class="box-border h-1px w-full bg-gray-50 px-4" />
<!-- 作者信息 -->
<view v-if="post?.owner" class="flex items-center gap-x-2 hidden!">
<view v-if="post?.owner" class="box-border flex items-center gap-x-2 hidden!">
<image
v-if="post.owner.avatar" class="h-8 w-8 border-2 border-white rounded-full border-solid"
:src="completeUrl(post.owner.avatar)" mode="aspectFill"
@@ -252,10 +243,12 @@ function handleCloseCommentPanel() {
</view>
<!-- 文章正文 -->
<mp-html
:content="htmlContent" :tag-style="markdownConfig.tagStyle"
:container-style="markdownConfig.containStyle" :domain="markdownConfig.domain" lazy-load selectable
/>
<view class="box-border px-1">
<mp-html
:content="htmlContent" :tag-style="markdownConfig.tagStyle"
:container-style="markdownConfig.containStyle" :domain="markdownConfig.domain" lazy-load selectable
/>
</view>
</view>
<!-- 加载中占位 -->
@@ -311,8 +304,24 @@ function handleCloseCommentPanel() {
<view class="fixed bottom-6 left-4 right-4 z-100 flex items-center gap-x-4" @touchmove.stop.prevent>
<!-- 左侧操作栏 -->
<view
class="uh-shadow-sm uh-backdrop-blur box-border h-46px flex flex-1 items-center justify-around border border-white rounded-full border-solid bg-white/95 p-1"
class="uh-shadow-md uh-backdrop-blur box-border h-46px flex flex-1 items-center justify-around border border-white rounded-full border-solid bg-white/95 p-1"
>
<!-- 返回 -->
<view class="flex flex-1 items-center justify-center gap-x-0.5" @click="handleBack">
<view
class="center rounded-full transition-colors"
>
<wd-icon
name="arrow-left"
color="#6B7280"
:size="18"
/>
</view>
<text class="text-xs text-gray-900 font-medium">
返回
</text>
</view>
<!-- 点赞 -->
<view class="flex flex-1 items-center justify-center gap-x-1" @click="handleUpvote">
<view
@@ -325,17 +334,17 @@ function handleCloseCommentPanel() {
:size="18"
/>
</view>
<text class="text-sm font-medium" :class="isUpvoted ? 'text-red-500' : 'text-gray-600'">
<text class="text-xs font-medium" :class="isUpvoted ? 'text-red-500' : 'text-gray-900'">
{{ stats.upvote }}
</text>
</view>
<!-- 评论 -->
<view class="flex flex-1 items-center justify-center gap-x-1" @click="handleOpenCommentPanel(post)">
<view class="flex flex-1 items-center justify-center gap-x-1" @click="handleOpenCommentPanel">
<view class="center rounded-full bg-transparent">
<wd-icon name="message" color="#6B7280" :size="18" />
</view>
<text class="text-sm text-gray-600 font-medium">{{ stats.comment }}</text>
<text class="text-xs text-gray-900 font-medium">{{ stats.comment }}</text>
</view>
<!-- 分享 -->
@@ -343,13 +352,13 @@ function handleCloseCommentPanel() {
<view class="center rounded-full bg-transparent">
<wd-icon name="share-alt" color="#6B7280" :size="18" />
</view>
<text class="text-sm text-gray-600 font-medium">分享</text>
<text class="text-xs text-gray-900 font-medium">分享</text>
</view>
</view>
<!-- 右侧返回顶部 -->
<view
class="uh-shadow-sm uh-backdrop-blur box-border h-46px w-46px center flex shrink-0 border border-white rounded-full border-solid bg-white/95"
class="uh-shadow-md uh-backdrop-blur box-border h-46px w-46px center flex shrink-0 border border-white rounded-full border-solid bg-white/95"
@click="handleScrollToTop"
>
<wd-icon name="arrow-up" color="#1A1A1A" :size="18" />
@@ -358,24 +367,12 @@ function handleCloseCommentPanel() {
</view>
<!-- 评论面板 -->
<uh-post-comment-panel v-if="commentPanel.show" :post="commentPanel.post" @close="handleCloseCommentPanel" />
<uh-post-comment-panel
v-if="showCommentPanel"
:post-name="postName"
:comment-subject-version="post?.spec?.releaseSnapshot ?? ''"
:comment-count="post?.stats?.comment ?? 0"
@close="showCommentPanel = false"
@commented="loadData"
/>
</template>
<style scoped>
.uh-shadow-xs {
box-shadow: 0 0 24rpx rgba(0, 0, 0, 0.035);
}
.uh-shadow-card {
box-shadow: 0 16rpx 60rpx rgba(0, 0, 0, 0.04);
}
.uh-shadow-sm {
box-shadow: 0 0 24rpx rgba(0, 0, 0, 0.075);
}
.uh-backdrop-blur {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
</style>
+22 -2
View File
@@ -1,5 +1,5 @@
import { completeUrl } from '@/utils/url'
import { getRandomUrl } from '@/utils/randomResources'
import { getCrAvatar, getRandomUrl } from '@/utils/randomResources'
/**
* 图片封面处理
@@ -7,10 +7,30 @@ import { getRandomUrl } from '@/utils/randomResources'
* @returns 处理后的图片封面
*/
export function cover(cover: string) {
const defaultCover = getRandomUrl()
if (!cover) {
return defaultCover
}
if (cover.trim()) {
return completeUrl(cover)
}
return getRandomUrl()
return defaultCover
}
/**
* 头像处理
* @param avatar 头像
* @returns 处理后的头像, 默认返回复古头像
*/
export function avatar(avatar: string) {
const defaultAvatar = getCrAvatar('retro')
if (!avatar) {
return defaultAvatar
}
if (avatar.trim()) {
return completeUrl(avatar)
}
return defaultAvatar
}
/**
+27 -2
View File
@@ -1,5 +1,3 @@
import { sleep } from '@/utils/common'
export const randomImageUrl = {
ycy: 'https://t.alcy.cc/ycy',
pc: 'https://t.alcy.cc/pc',
@@ -7,6 +5,7 @@ export const randomImageUrl = {
picsum: 'https://picsum.photos/800/600',
bing: 'https://bing.img.run/rand_uhd.php',
xsot: 'https://api.xsot.cn/bing?jump=true',
boxmoe: 'https://api.boxmoe.com/random.php',
}
type randomUrlKeys = keyof typeof randomImageUrl
@@ -15,6 +14,12 @@ export const randomVideosUrl = {
acg: 'https://t.alcy.cc/acg',
}
/**
* 获取 Picsum 随机图片URL
* @param width 图片宽度 默认800
* @param height 图片高度 默认600
* @returns 随机图片URL
*/
export function getPicSumImages(width: number = 800, height?: number) {
const t = `${Date.now()}+${Math.random()}`
if (!height) {
@@ -23,6 +28,26 @@ export function getPicSumImages(width: number = 800, height?: number) {
return `https://picsum.photos/${width}/${height}?t=${t}`
}
/**
* 获取 DiceURL
* @param avatar 头像名称 可选值:lorelei=简约卡通人脸, pixel-art=像素小人, adventurer=可爱大头, avataaars=极简线条
* @returns 头像URL
*/
export function getDiceBearAvatar(avatar: 'lorelei' | 'pixel-art' | 'adventurer' | 'avataaars', size?: number) {
const t = `${Date.now()}+${Math.random()}`
return `https://api.dicebear.com/9.x/${avatar}/svg?size=${size || 128}&random=${t}`
}
/**
* 获取 CravatarURL
* @param avatar 头像名称 可选值:monsterid=怪物头像, retro=复古头像, identicon=图标头像
* @returns 头像URL
*/
export function getCrAvatar(avatar: 'monsterid' | 'retro' | 'identicon', size?: number) {
const id = `${Date.now()}+${Math.random()}`
return `https://cravatar.cn/avatar/${id}?d=${avatar}&s=${size || 128}`
}
/**
* 获取随机图片URL
* @param type 图片类型 默认pc