Commit 96bad3c0 by lijiabin

【需求 20331】 feat: 文章详情页(帖子等相关、视频、问吧)加入骨架屏

parent e6d3866d
<template>
<div
class="bg-white backdrop-blur-sm rounded-lg shadow-sm border border-white/50 overflow-hidden"
class="p-6 bg-white backdrop-blur-sm rounded-lg shadow-sm border border-white/50 overflow-hidden"
>
<el-skeleton :rows="5" animated :loading="loading" :throttle="{ leading: 0, trailing: 1500 }">
<template #template>
<!-- 发布者信息 -->
<div class="p-6 border-b border-gray-100 pb-0">
<div class="border-b border-gray-100 pb-0">
<div class="flex items-center gap-4">
<!-- 头像 -->
<el-skeleton-item variant="circle" style="width: 48px; height: 48px" />
<!-- 用户名 + 时间 -->
<div class="flex-1">
<el-skeleton-item variant="text" style="width: 120px; height: 16px" />
<el-skeleton-item
variant="text"
style="width: 220px; height: 14px; margin-top: 6px"
/>
</div>
<!-- 编辑 -->
<!-- <el-skeleton-item variant="text" style="width: 40px; height: 16px" /> -->
<!-- 右侧标签 / 操作 -->
<div class="flex items-center gap-3">
<el-skeleton-item
variant="text"
style="width: 72px; height: 28px; border-radius: 6px"
/>
<el-skeleton-item
variant="text"
style="width: 72px; height: 28px; border-radius: 6px"
/>
<!-- <el-skeleton-item variant="circle" style="width: 24px; height: 24px" /> -->
</div>
</div>
</div>
<!-- 正文区域 -->
<div class="mt-6">
<!-- 标题 -->
<el-skeleton-item variant="text" style="width: 90%; height: 32px; margin-bottom: 16px" />
<!-- 顶部视频占位 -->
<!-- <el-skeleton-item
variant="image"
style="width: 80%; height: 320px; margin: 0 auto 24px; border-radius: 8px"
/> -->
<!-- 文章内容 -->
<div class="space-y-3">
<el-skeleton-item variant="text" style="width: 100%" />
<el-skeleton-item variant="text" style="width: 96%" />
<el-skeleton-item variant="text" style="width: 92%" />
<el-skeleton-item variant="text" style="width: 88%" />
</div>
<!-- 图片内容 -->
<!-- <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-6">
<el-skeleton-item
v-for="i in 2"
:key="i"
variant="image"
style="width: 100%; height: 256px; border-radius: 12px"
/>
</div> -->
<!-- 底部视频 -->
<!-- <el-skeleton-item
variant="image"
style="width: 80%; height: 320px; margin: 24px auto; border-radius: 8px"
/> -->
<!-- 标签 -->
<div class="flex flex-wrap gap-2 mt-6">
<el-skeleton-item
v-for="i in 3"
:key="i"
variant="text"
style="width: 72px; height: 28px; border-radius: 9999px"
/>
</div>
</div>
</template>
<template #default>
<!-- 发布者信息 -->
<div class="border-b border-gray-100 pb-0">
<div class="flex items-center gap-4">
<div class="relative">
<img
......@@ -14,7 +96,9 @@
jumpToUserHomePage({
userId: articleDetail?.createUserId,
isReal:
articleDetail.type === 'practice' || articleDetail.type === 'interview' ? 1 : 0,
articleDetail.type === 'practice' || articleDetail.type === 'interview'
? 1
: 0,
})
"
/>
......@@ -44,7 +128,9 @@
v-if="isAuthor"
type="primary"
:underline="false"
@click="router.push(`/publishLongArticle/${articleDetail.type}?id=${articleDetail.id}`)"
@click="
router.push(`/publishLongArticle/${articleDetail.type}?id=${articleDetail.id}`)
"
class="text-sm"
>
编辑
......@@ -69,7 +155,7 @@
</div>
</div>
<div class="p-6">
<div class="mt-6">
<h1 class="text-2xl font-bold text-gray-900 mb-4 leading-tight">
{{ articleDetail?.title }}
</h1>
......@@ -78,7 +164,7 @@
<video
:src="articleDetail.articleVideoUrl"
controls
class="w-80% aspect-video bg-black"
class="w-100%! aspect-video bg-black"
:poster="`${articleDetail.articleVideoUrl}?x-oss-process=video/snapshot,t_1000,f_jpg`"
/>
</div>
......@@ -107,7 +193,7 @@
<video
:src="articleDetail.videoUrl"
controls
class="w-80% aspect-video bg-black"
class="w-100%! aspect-video bg-black"
:poster="`${articleDetail.videoUrl}?x-oss-process=video/snapshot,t_1000,f_jpg`"
/>
</div>
......@@ -121,7 +207,7 @@
<video
:src="articleDetail.articleVideoUrl"
controls
class="w-80% aspect-video bg-black"
class="w-100%! aspect-video bg-black"
:poster="`${articleDetail.articleVideoUrl}?x-oss-process=video/snapshot,t_1000,f_jpg`"
/>
</div>
......@@ -136,6 +222,8 @@
</span>
</div>
</div>
</template>
</el-skeleton>
<!-- 富文本内容的 图片预览 -->
<el-image-viewer
v-if="showPreview"
......@@ -168,6 +256,9 @@ const { articleDetail } = defineProps<{
const articleType = computed(() => {
return articleTypeListOptions.find((item) => item.value === articleDetail.type)?.label
})
const loading = computed(() => !articleDetail.title)
// 是否是作者
const isAuthor = computed(() => {
return articleDetail.createUserId === userInfo.value.userId
......
......@@ -5,6 +5,76 @@
<div
class="bg-white rounded-lg p-6 shadow-[0_1px_3px_rgba(0,0,0,0.02)] border border-slate-100"
>
<el-skeleton
:rows="5"
animated
:loading="loading"
:throttle="{ leading: 0, trailing: 1500 }"
>
<template #template>
<!-- 顶部标签行 -->
<div class="flex flex-wrap gap-2 mb-4 justify-between">
<!-- 标签 -->
<div class="flex gap-2">
<el-skeleton-item
v-for="i in 3"
:key="i"
variant="text"
style="width: 60px; height: 18px; border-radius: 9999px"
/>
</div>
<!-- 编辑按钮 -->
<el-skeleton-item variant="text" style="width: 40px; height: 18px" />
</div>
<!-- 发布人信息 -->
<div class="flex items-center gap-3 pb-3 border-b border-slate-100">
<!-- 头像 -->
<el-skeleton-item variant="circle" style="width: 40px; height: 40px" />
<!-- 用户名 + 时间 -->
<div class="flex flex-col gap-2">
<el-skeleton-item variant="text" style="width: 100px; height: 14px" />
<el-skeleton-item variant="text" style="width: 180px; height: 12px" />
</div>
</div>
<!-- 标题 -->
<div class="mt-3 mb-3">
<el-skeleton-item variant="text" style="width: 90%; height: 28px" />
</div>
<!-- 内容描述 -->
<div class="space-y-2">
<el-skeleton-item variant="text" style="width: 100%" />
<el-skeleton-item variant="text" style="width: 95%" />
<el-skeleton-item variant="text" style="width: 85%" />
</div>
<!-- 图片列表 -->
<!-- <div class="mt-3 flex gap-2 flex-wrap">
<el-skeleton-item
v-for="i in 3"
:key="i"
variant="image"
style="width: 96px; height: 96px; border-radius: 8px"
/>
</div> -->
<!-- 底部操作栏 -->
<div class="flex items-center justify-between mt-4">
<!-- 写回答按钮 -->
<el-skeleton-item variant="button" style="width: 88px; height: 32px" />
<!-- 右侧点赞 / 收藏 -->
<div class="flex items-center gap-6">
<el-skeleton-item variant="text" style="width: 48px; height: 16px" />
<el-skeleton-item variant="text" style="width: 48px; height: 16px" />
</div>
</div>
</template>
<template #default>
<!-- 顶部标签行 -->
<div class="flex flex-wrap gap-2 mb-4 justify-between">
<div>
......@@ -107,7 +177,9 @@
class="hover:text-slate-600 cursor-pointer transition-colors flex items-center gap-1"
>
<el-icon>
<svg-icon :name="questionDetail?.hasPraised ? 'praise_fill' : 'praise'"></svg-icon>
<svg-icon
:name="questionDetail?.hasPraised ? 'praise_fill' : 'praise'"
></svg-icon>
</el-icon>
<span :class="{ 'text-blue-500': questionDetail?.hasPraised }">{{
questionDetail?.praiseCount || 0
......@@ -128,7 +200,8 @@
</span>
</div>
</div>
<!-- 展示图片相关 -->
</template>
</el-skeleton>
</div>
<!-- 2. 列表控制栏 -->
......@@ -331,6 +404,8 @@ const commentRefList = ref<InstanceType<typeof Comment>[]>([])
const questionDetail = ref<ArticleItemDto>({} as ArticleItemDto)
const commentDialogRef = useTemplateRef<typeof CommentDialog>('commentDialogRef')
const loading = computed(() => !questionDetail.value.title)
const isAuthor = computed(() => {
return questionDetail.value.createUserId === userInfo.value.userId
})
......
......@@ -54,12 +54,20 @@
</div>
<!-- 余额提示 -->
<div class="text-center text-12px text-gray-400 mt-4">当前余额: {{ balance }}</div>
<div class="text-center text-12px text-gray-400 mt-4">
当前余额: {{ yabiData.currentValue }}
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { addOrCancelArticleReward, getYaBiData } from '@/api'
import { addOrCancelArticleReward } from '@/api'
import { useYaBiStore } from '@/stores'
import { storeToRefs } from 'pinia'
const yabiStore = useYaBiStore()
const { yabiData } = storeToRefs(yabiStore)
const rewardNum = defineModel<number>('rewardNum', { required: true, default: 0 })
interface RewardOption {
amount: number
......@@ -69,7 +77,6 @@ interface RewardOption {
const dialogVisible = ref(false)
const selectedAmount = ref(2)
const balance = ref(0)
let articleId = -1
......@@ -82,12 +89,7 @@ const rewardOptions = ref<RewardOption[]>([
const open = (id: number) => {
articleId = id
dialogVisible.value = true
getYaBiDataFn()
}
const getYaBiDataFn = async () => {
const { data } = await getYaBiData()
balance.value = data.currentValue
yabiStore.fetchYaBiData()
}
// 选择金额
......@@ -100,7 +102,7 @@ const selectAmount = (amount: number) => {
// 确认打赏
const handleConfirm = async () => {
if (balance.value < selectedAmount.value) {
if (yabiData.value.currentValue < selectedAmount.value) {
ElMessage.warning('余额不足,请先充值')
return
}
......@@ -112,6 +114,7 @@ const handleConfirm = async () => {
ElMessage.success('打赏成功!')
dialogVisible.value = false
rewardNum.value += selectedAmount.value
yabiStore.fetchYaBiData()
}
defineExpose({
......
......@@ -4,6 +4,69 @@
<!-- 整体页面容器:浅灰背景 -->
<!-- 卡片1: 视频播放器区域 -->
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
<el-skeleton :rows="5" animated :loading="loading" :throttle="{ leading: 0, trailing: 500 }">
<template #template>
<!-- 标题区 -->
<div class="p-4 pb-3">
<div class="flex items-center justify-between">
<el-skeleton-item variant="text" style="width: 60%; height: 24px" />
<el-skeleton-item variant="button" style="width: 32px; height: 32px" />
</div>
<!-- 时间 / 播放 / 地区 -->
<div class="flex gap-2 mt-3">
<el-skeleton-item variant="text" style="width: 160px" />
<el-skeleton-item variant="text" style="width: 80px" />
<el-skeleton-item variant="text" style="width: 60px" />
</div>
</div>
<!-- 视频区域 16:9 -->
<div class="w-full aspect-video px-4">
<el-skeleton-item variant="image" style="width: 100%; height: 100%" />
</div>
<!-- UP主 + 操作区 -->
<div class="bg-white rounded-xl shadow-sm p-4">
<div class="flex items-center justify-between flex-wrap gap-4">
<!-- 左侧 UP主 -->
<div class="flex items-center gap-3">
<el-skeleton-item variant="circle" style="width: 48px; height: 48px" />
<div>
<el-skeleton-item variant="text" style="width: 120px; height: 16px" />
</div>
</div>
<!-- 右侧按钮区 -->
<div class="flex items-center gap-4">
<!-- 点赞 -->
<div class="flex items-center gap-2">
<el-skeleton-item variant="circle" style="width: 24px; height: 24px" />
<el-skeleton-item variant="text" style="width: 24px" />
</div>
<!-- 收藏 -->
<div class="flex items-center gap-2">
<el-skeleton-item variant="circle" style="width: 24px; height: 24px" />
<el-skeleton-item variant="text" style="width: 24px" />
</div>
<!-- 评论 -->
<div class="flex items-center gap-2">
<el-skeleton-item variant="circle" style="width: 24px; height: 24px" />
<el-skeleton-item variant="text" style="width: 24px" />
</div>
<!-- 打赏按钮 -->
<el-skeleton-item variant="button" style="width: 100px; height: 36px" />
<!-- 更多 -->
<el-skeleton-item variant="circle" style="width: 32px; height: 32px" />
</div>
</div>
</div>
</template>
<template #default>
<!-- 标题区 -->
<div class="p-4 pb-3">
<h1
......@@ -17,7 +80,9 @@
{{ dayjs(videoDetail?.createTime * 1000).format('YYYY-MM-DD HH:mm') }}
</span>
·
<span class="flex items-center"> {{ formatNumber(videoDetail?.playCount) }}播放 </span>
<span class="flex items-center">
{{ formatNumber(videoDetail?.playCount) }}播放
</span>
·
<span class="flex items-center">
{{ videoDetail?.region }}
......@@ -26,11 +91,11 @@
</div>
<!-- 视频 16/ 9 -->
<div class="w-full bg-black aspect-video">
<div class="aspect-video mx-4">
<video
ref="videoRef"
:src="videoDetail?.videoUrl"
class="w-full aspect-video object-contain"
class="aspect-video object-contain"
controls
@play="handlePlay"
@pause="handlePause"
......@@ -200,6 +265,8 @@
</div>
</div>
</div>
</template>
</el-skeleton>
</div>
<!-- 卡片3: 简介与标签 -->
......@@ -258,6 +325,7 @@ const videoId = Number(route.params.id)
// 视频详情
const videoDetail = ref({} as ArticleItemDto)
const loading = computed(() => !videoDetail.value.title)
const commentRef = useTemplateRef<InstanceType<typeof Comment> | null>('commentRef')
const rewardDialogRef = useTemplateRef<InstanceType<typeof RewardDialog> | null>('rewardDialogRef')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment