Files
test-subject/src/pages/post.vue
2025-10-27 15:03:40 +08:00

502 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view v-if="loading" class="page">
<Head></Head>
<!-- 内容区 -->
<view class="content">
<!-- 动态区域 -->
<view class="moment">
<!-- 用户栏 -->
<view class="userbar">
<image v-if="post.user && post.user.userImg" :src="post.user.userImg" mode="aspectFill" class="userimg"
alt="用户头像" />
<image v-else src="/static/imgs/default-avatar.png" mode="aspectFill" class="userimg" alt="默认头像" />
<text class="username">{{ post.user ? post.user.nickName : '未知用户' }}</text>
<button class="follow" @tap="common.openapp">
<uni-icons v-if="post.isfollow" type="checkmarkempty" size="20" color="#333"></uni-icons>
<text v-else>关注</text>
</button>
</view>
<!-- 轮播图 -->
<swiper v-if="post.imgs && post.imgs.length > 0" :indicator-dots="false" :autoplay="false" :interval="3000"
:duration="1000" :circular="true" class="swiper-banner" @change="onChange">
<swiper-item v-for="(item, i) in post.imgs" :key="i">
<view class="swiper-center">
<image v-if="item.type === 'img'" :src="item.src" class="swiper-item" mode="aspectFit" alt="动态图片内容"
@load="onImageLoad(i)" @error="onImageError(i)" />
<video v-else-if="item.type === 'video'" id="videoid" :src="item.src" class="swiper-item"
:controls="false" @tap="pausevideo" object-fit="contain" alt="动态视频内容" />
<!-- 播放按钮 - 使用浏览器复制的样式 -->
<view v-if="!isPlaying" class="uni-video-cover" @tap="pausevideo">
<view class="uni-video-cover-play-button uni-video-icon"></view>
</view>
<!-- <cover-view class="swiper-mask" @tap="common.openapp">
</cover-view> -->
<!-- 图片加载状态 -->
<!-- <view v-if="item.loading" class="image-loading">
<text>图片加载中...</text>
</view>
<view v-if="item.error" class="image-error">
<text>图片加载失败</text>
</view> -->
</view>
</swiper-item>
</swiper>
<!-- 外部指示点:2个及以上才显示 -->
<view v-if="post.imgs && post.imgs.length > 1" class="dots-bar">
<view v-for="(dot, i) in post.imgs" :key="i" class="dot" :class="{ active: i === current }" />
</view>
<!-- 底部文案 -->
<view v-if="post.copywriting && post.date" class="momentbottom">
<text class="copywriting">{{ post.copywriting }}</text>
<uni-dateformat :date="Date.parse(post.date.replace(/-/g, '/'))" :threshold="[0, 0]" format="yyyy-MM-dd"
class="date-text" />
</view>
</view>
</view>
<!-- 评论区域 + 互动区域 -->
<Comments :postid="post.id" />
<!-- 互动区域 -->
<!-- <Intereact :collectsum="post.collectsum" :likesum="post.likesum" /> -->
<!-- Findmore -->
<Findmore />
</view>
<view v-else class="loading-container">
<text>页面加载中...</text>
</view>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useCommonStore } from '@/stores/common.js'
import Head from '@/pages/head/head.vue'
import Comments from '@/pages/comments/comments.vue'
import Findmore from '@/pages/findmore/findmore.vue'
import { getPostList, getPostLImage, getPostVideo, getUserImg } from '../api/api.js'
const common = useCommonStore()
// 当前 dot
const current = ref(0)
const onChange = e => current.value = e.detail.current
// 动态数据
const post = ref({})
// 播放状态
const isPlaying = ref(true) // 默认播放状态
// 资源加载状态
const loading = ref(false)
// 图片加载成功处理
const onImageLoad = (index) => {
if (post.value.imgs && post.value.imgs[index]) {
post.value.imgs[index].loading = false
post.value.imgs[index].error = false
}
}
// 图片加载失败处理
const onImageError = (index) => {
if (post.value.imgs && post.value.imgs[index]) {
post.value.imgs[index].loading = false
post.value.imgs[index].error = true
console.warn(`图片加载失败: index ${index}`)
}
}
//暂停/播放
const pausevideo = () => {
// 获取VideoContext实例
const videoCtx = uni.createVideoContext('videoid', getCurrentInstance());
// 检查视频状态并切换播放/暂停
if (videoCtx) {
// 使用一个状态变量来跟踪播放状态
if (isPlaying.value) {
videoCtx.pause()
isPlaying.value = false
} else {
videoCtx.play()
isPlaying.value = true
}
}
}
// 组件挂载时获取数据
onLoad(() => {
const params = ''
getPostList(params).then(res => {
try {
// console.log(res.data.data)
const data = res.data.data
// 处理帖子相关字段 - 根据实际API返回字段优化
data.id = data.id || ''
data.title = data.newsTitle || data.title || ''
data.source = data.newsSource || data.source || ''
data.copywriting = data.textContent || data.newsContent || ''
data.date = data.time || data.date || ''
data.countLike = data.likeCount || data.countLike || 0
data.collectsum = data.collectsum || 0
// data.user.nickName = data.user.nickName || ''
// data.user.userImg = data.user.avatar || data.userImg || ''
// data.comments = data.comments || []
const mediaPromises = []
// 处理图片资源
if (data.imgs && Array.isArray(data.imgs)) {
data.imgs.forEach(image => {
mediaPromises.push(getPostLImage(image.original_url).then(imageRes => {
if (imageRes.statusCode === 200 && imageRes.data) {
// 将arraybuffer转换为base64
const base64 = uni.arrayBufferToBase64(imageRes.data)
const imageUrl = 'data:image/webp;base64,' + base64
return {
type: 'img',
src: imageUrl,
original_url: image.original_url,
loading: true, // 初始加载状态
error: false // 错误状态
}
}
return null
}).catch(error => {
uni.showToast({
title: error.message,
icon: 'error'
})
return null
}))
})
}
//处理视频资源
if (data.videos && Array.isArray(data.videos)) {
data.videos.forEach(video => {
mediaPromises.push(getPostVideo(video.original_url).then(videoRes => {
if (videoRes.statusCode === 200 && videoRes.data) {
// 将arraybuffer转换为base64
const base64 = uni.arrayBufferToBase64(videoRes.data)
const videoUrl = 'data:video/mp4;base64,' + base64
return {
type: 'video',
src: videoUrl,
original_url: video.original_url,
loading: true, // 初始加载状态
error: false // 错误状态
}
}
return null
}).catch(error => {
uni.showToast({
title: error.message,
icon: 'error'
})
return null
}))
})
}
//处理用户信息
if (data.user && data.user.constructor === Object) {
// 如果有头像路径,则获取头像资源
const avatarPath = data.user.avatar || data.userImg
if (avatarPath) {
mediaPromises.push(getUserImg(avatarPath).then(userImgRes => {
if (userImgRes.statusCode === 200 && userImgRes.data) {
// 将arraybuffer转换为base64
const base64 = uni.arrayBufferToBase64(userImgRes.data)
const userImgUrl = 'data:image/webp;base64,' + base64
return {
type: 'userImg',
src: userImgUrl,
original_url: avatarPath
}
}
return null
}).catch(error => {
console.warn('用户头像加载失败:', error)
return null
}))
}
data.user = {
nickName: data.user.nickName || '',
userImg: avatarPath || '' // 先使用原始路径后续会替换为base64
}
}
// 统一等待所有媒体资源加载完成
if (mediaPromises.length > 0) {
Promise.all(mediaPromises).then(mediaItems => {
// 过滤掉null值将有效的媒体资源合并到imgs数组中
const validMediaItems = mediaItems.filter(item => item !== null)
// 分离用户头像和其他媒体资源
const userImgItem = validMediaItems.find(item => item.type === 'userImg')
const otherMediaItems = validMediaItems.filter(item => item.type !== 'userImg')
// 如果有用户头像资源,更新用户头像
if (userImgItem) {
data.user.userImg = userImgItem.src
}
// 设置其他媒体资源
data.imgs = otherMediaItems
// post.value = data
console.log(data)
Object.assign(post.value, data)
loading.value = true
})
} else {
// 如果没有媒体资源,设置空数组
data.imgs = []
post.value = data
}
} catch (error) {
handleError('数据处理错误:', error, '数据加载失败', '抱歉,新闻内容加载失败,请稍后重试')
}
}).catch(error => {
handleError('网络请求错误:', error, '网络错误', '网络连接异常,请检查网络设置')
})
// 统一的错误处理函数
const handleError = (errorType, error, title, message) => {
console.error(errorType, error)
post.value = {
title: title,
source: '系统',
copywriting: message,
date: new Date().toISOString(),
countLike: 0,
collectsum: 0,
comments: [],
imgs: []
}
}
})
</script>
<style scoped>
page,
.page {
width: 100%;
max-width: 430px;
height: auto;
display: flex;
flex-direction: column;
margin: 0 auto;
position: relative;
}
.content {
flex-shrink: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
.moment {
width: 100%;
flex-shrink: 0;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.userbar {
width: 100%;
padding: 16px 16px 8px;
display: flex;
align-items: center;
box-sizing: border-box;
gap: 10px
}
.userimg {
width: 34px;
height: 34px;
border-radius: 50%;
}
.username {
flex: 1;
font-size: 17px;
font-weight: 600;
color: #333
}
.follow {
width: 60px;
height: 32px;
flex-shrink: 0;
font-size: 14px;
font-weight: 600;
line-height: 32px;
border-radius: 8px;
background-color: rgba(51, 51, 51, .05);
display: flex;
align-items: center;
justify-content: center;
}
.follow::after {
border: none;
}
.swiper-banner {
width: 100%;
height: 357px
}
swiper-item {
display: flex;
align-items: center;
justify-content: center
}
.swiper-center {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.swiper-item {
width: 100%;
height: 100%
}
::v-deep .uni-video-cover {
background-color: transparent;
}
::v-deep .uni-video-cover-play-button {
width: 100% !important;
height: 100% !important;
display: flex;
align-items: center;
justify-content: center;
}
.uni-video-cover {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 1;
}
.uni-video-icon {
font-family: 'uni-video-icon';
text-align: center;
}
.uni-video-cover-play-button {
width: 100% !important;
height: 100% !important;
display: flex;
align-items: center;
justify-content: center;
line-height: 75px;
font-size: 56px;
color: rgba(255, 255, 255, 0.5);
cursor: pointer;
}
.uni-video-cover-play-button::after {
content: '\ea24';
}
.uni-video-cover {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 1;
}
.dots-bar {
display: flex;
justify-content: center;
gap: 6px;
padding: 8px 0 0;
}
.dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(0, 0, 0, .2);
transition: background .3s
}
.dot.active {
background: #333
}
.momentbottom {
padding: 16px 16px 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.copywriting {
font-size: 15px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: normal;
letter-spacing: normal;
text-align: left;
color: #000;
}
.date-text {
display: flex;
align-items: center;
height: 17px;
line-height: 17px;
font-size: 12px;
color: #b1aeb2;
}
.loading-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
</style>