addapi
102
src/api/api.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* 获取H5帖子列表
|
||||||
|
* @param {Object} data - 请求参数
|
||||||
|
* @returns {Promise} uni.request返回的Promise对象
|
||||||
|
*/
|
||||||
|
export const getPostList = data => {
|
||||||
|
return uni.request({
|
||||||
|
url: '/api/v1/h5-posts',
|
||||||
|
method: 'get',
|
||||||
|
data: data,
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'platform': 'H5'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPostLImage = filename => {
|
||||||
|
return uni.request({
|
||||||
|
url: '/api/v1/h5-content/' + filename,
|
||||||
|
method: 'get',
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'platform': 'H5'
|
||||||
|
},
|
||||||
|
responseType: 'arraybuffer',
|
||||||
|
timeout: 60000 // 添加超时设置
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPostVideo = filename => {
|
||||||
|
// 如果filename已经是完整路径,直接使用;否则拼接路径
|
||||||
|
let url = filename
|
||||||
|
if (!filename.startsWith('/')) {
|
||||||
|
url = '/api/v1/h5-video/' + filename
|
||||||
|
}
|
||||||
|
return uni.request({
|
||||||
|
url: url,
|
||||||
|
method: 'get',
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'platform': 'H5'
|
||||||
|
},
|
||||||
|
responseType: 'arraybuffer',
|
||||||
|
timeout: 60000 // 添加超时设置
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户头像图片
|
||||||
|
* @param {string} filename - 图片文件名或完整路径
|
||||||
|
* @returns {Promise} uni.request返回的Promise对象
|
||||||
|
*/
|
||||||
|
export const getUserImg = filename => {
|
||||||
|
// 处理filename,只保留default_avatar.png字段
|
||||||
|
let processedFilename = filename
|
||||||
|
|
||||||
|
// 如果传入的是完整路径,提取文件名
|
||||||
|
if (filename.includes('/')) {
|
||||||
|
const parts = filename.split('/')
|
||||||
|
processedFilename = parts[parts.length - 1]
|
||||||
|
}
|
||||||
|
const url = '/api/v1/h5-avatar/' + processedFilename
|
||||||
|
// console.log(url)
|
||||||
|
return uni.request({
|
||||||
|
url: url,
|
||||||
|
method: 'GET',
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'platform': 'H5'
|
||||||
|
},
|
||||||
|
responseType: 'arraybuffer',
|
||||||
|
timeout: 60000 // 添加超时设置
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取H5评论列表
|
||||||
|
* @param {Object} params - 请求参数
|
||||||
|
* @returns {Promise} uni.request返回的Promise对象
|
||||||
|
*/
|
||||||
|
export const getCommentList = params => {
|
||||||
|
return uni.request({
|
||||||
|
url: '/api/v1/h5-comments?postId=' + params.postId,
|
||||||
|
method: 'get',
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'platform': 'H5'
|
||||||
|
},
|
||||||
|
timeout: 60000 // 添加超时设置
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认导出所有接口
|
||||||
|
export default {
|
||||||
|
getPostList,
|
||||||
|
getCommentList,
|
||||||
|
getPostLImage,
|
||||||
|
getPostVideo,
|
||||||
|
getUserImg
|
||||||
|
}
|
||||||
@@ -9,13 +9,13 @@
|
|||||||
{
|
{
|
||||||
"path": "pages/post",
|
"path": "pages/post",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "帖子"
|
"navigationBarTitleText": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/video",
|
"path": "pages/video",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "视频"
|
"navigationBarTitleText": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -39,7 +39,13 @@
|
|||||||
{
|
{
|
||||||
"path": "pages/news",
|
"path": "pages/news",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "新闻"
|
"navigationBarTitleText": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/intereact/intereact",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 评论区域 -->
|
<!-- 评论区域 -->
|
||||||
<view v-if="comments" class="comment">
|
<view class="comment">
|
||||||
|
<main>
|
||||||
<!-- 评论头部信息 -->
|
<!-- 评论头部信息 -->
|
||||||
<view class="commenthead">
|
<view class="commenthead">
|
||||||
<text class="commentcount">{{ totalCommentCount }}条评论</text>
|
<text class="commentcount">{{ comments.length }}条评论</text>
|
||||||
<view class="headswitch">
|
<view class="headswitch">
|
||||||
<text class="inact" @tap="handleOpenApp">默认</text>
|
<text class="inact" @tap="handleOpenApp">默认</text>
|
||||||
<view class="act" @tap="handleOpenApp">最新</view>
|
<view class="act" @tap="handleOpenApp">最新</view>
|
||||||
@@ -11,10 +12,13 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 评论主体 -->
|
<!-- 评论主体 -->
|
||||||
<view v-for="commentItem in highlightedList" :key="commentItem.id" class="commentdetail" @tap="onDelegateTap">
|
|
||||||
|
<view v-if="comments.list.length > 0">
|
||||||
|
|
||||||
|
<view v-for="commentItem in comments.list" :key="commentItem.id" class="commentdetail" @tap="onDelegateTap">
|
||||||
<!-- 头像 -->
|
<!-- 头像 -->
|
||||||
<view class="commentdetailleft">
|
<view class="commentdetailleft">
|
||||||
<image src="/static/logo.png" mode="aspectFill" alt="用户头像"></image>
|
<image :src="commentItem.userAvatar" mode="aspectFill" alt="用户头像"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 右侧主评论 + 子评论 + 展开条 -->
|
<!-- 右侧主评论 + 子评论 + 展开条 -->
|
||||||
@@ -25,8 +29,10 @@
|
|||||||
<text class="commentusername">{{ commentItem.userName }}</text>
|
<text class="commentusername">{{ commentItem.userName }}</text>
|
||||||
<rich-text class="commentusercontent" :nodes="commentItem.renderNodes" />
|
<rich-text class="commentusercontent" :nodes="commentItem.renderNodes" />
|
||||||
<view class="date-reply">
|
<view class="date-reply">
|
||||||
<uni-dateformat :date="Date.parse(commentItem.date.replace(/-/g, '/'))" :threshold="[0, 0]"
|
<uni-dateformat v-if="commentItem.createdAt"
|
||||||
format="yyyy-MM-dd" class="date-text" />
|
:date="Date.parse(commentItem.createdAt.replace(/-/g, '/'))" :threshold="[0, 0]" format="yyyy-MM-dd"
|
||||||
|
class="date-text" />
|
||||||
|
<text v-else class="date-text">未知时间</text>
|
||||||
<text class="replytext" @tap.stop="handleOpenApp">回复</text>
|
<text class="replytext" @tap.stop="handleOpenApp">回复</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -41,14 +47,15 @@
|
|||||||
<view v-if="commentItem.showChild" class="commentchildcontainer">
|
<view v-if="commentItem.showChild" class="commentchildcontainer">
|
||||||
<view v-for="child in commentItem.children" :key="child.id" class="commentchild">
|
<view v-for="child in commentItem.children" :key="child.id" class="commentchild">
|
||||||
<view class="commentchildleft">
|
<view class="commentchildleft">
|
||||||
<image src="/static/logo.png" mode="aspectFill" alt="用户头像"></image>
|
<image :src="child.userAvatar || '/static/logo.png'" mode="aspectFill" alt="用户头像"></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="commentchildright">
|
<view class="commentchildright">
|
||||||
<text class="commentusername">{{ child.userName }}</text>
|
<text class="commentusername">{{ child.userName }}</text>
|
||||||
<rich-text class="commentusercontent" :nodes="child.renderNodes" />
|
<rich-text class="commentusercontent" :nodes="child.renderNodes" />
|
||||||
<view class="date-reply">
|
<view class="date-reply">
|
||||||
<uni-dateformat :date="Date.parse(child.date.replace(/-/g, '/'))" :threshold="[0, 0]"
|
<uni-dateformat v-if="child.date" :date="Date.parse(child.date.replace(/-/g, '/'))"
|
||||||
format="yyyy-MM-dd" class="date-text" />
|
:threshold="[0, 0]" format="yyyy-MM-dd" class="date-text" />
|
||||||
|
<text v-else class="date-text">未知时间</text>
|
||||||
<text class="replytext" @tap.stop="handleOpenApp">回复</text>
|
<text class="replytext" @tap.stop="handleOpenApp">回复</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -60,70 +67,65 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 展开/收起按钮 -->
|
<!-- 展开/收起按钮 -->
|
||||||
<view v-if="commentItem.children.length" class="expandcomment">
|
<view v-if="commentItem.reply.length" class="expandcomment">
|
||||||
<view style="width:20px;height:1px;background:rgba(65,60,67,.2)"></view>
|
<view style="width:20px;height:1px;background:rgba(65,60,67,.2)"></view>
|
||||||
<text class="expandcommenttext" :data-cid="commentItem.id">
|
<text class="expandcommenttext" :data-cid="commentItem.id">
|
||||||
{{ commentItem.showChild ? '收起' : `展开${commentItem.children.length}条回复` }}
|
{{ commentItem.showChild ? '收起' : `展开${commentItem.reply.length}条回复` }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 占位 -->
|
|
||||||
|
<view v-else class="nocomments">
|
||||||
|
<image src="/static/imgs/nocomments/nocomments@3x.webp" mode="aspectFit" alt="暂无评论"></image>
|
||||||
|
<text class="nocommentstext">空空如也~</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 占位视图 -->
|
||||||
<view class="spacerview"></view>
|
<view class="spacerview"></view>
|
||||||
|
</main>
|
||||||
<!-- 互动区域 -->
|
<!-- 互动区域 -->
|
||||||
<view v-if="showInteraction" class="interaction">
|
<Intereact />
|
||||||
<view class="editarea" @tap="handleOpenApp">
|
|
||||||
<image src="/static/imgs/editicon/icon@2x.webp" mode="aspectFit" class="editicon" alt="编辑标签"></image>
|
|
||||||
<text class="edittext">快来互动吧…</text>
|
|
||||||
</view>
|
|
||||||
<view class="spacerview"></view>
|
|
||||||
<view class="collection" @tap="handleOpenApp">
|
|
||||||
<image src="@/static/imgs/staricon/icon@3x.webp" mode="aspectFit" class="collectionicon" alt="收藏标签"></image>
|
|
||||||
<text class="collectioncount">{{ formatCount(collectsum) }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="spacerview"></view>
|
|
||||||
<view class="like" @tap="handleOpenApp">
|
|
||||||
<image src="@/static/imgs/likeicon/icon@3x.webp" mode="aspectFit" class="likeicon" alt="点赞标签"></image>
|
|
||||||
<text class="likecount">{{ formatCount(likesum) }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- Findmore -->
|
||||||
|
<Findmore />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
import { reactive } from 'vue'
|
||||||
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
import { getCommentList, getUserImg } from '@/api/api.js'
|
||||||
import { useCommonStore } from '@/stores/common.js'
|
import { useCommonStore } from '@/stores/common.js'
|
||||||
|
import Findmore from '@/pages/findmore/findmore.vue'
|
||||||
const props = defineProps({
|
import Intereact from '@/pages/intereact/intereact.vue'
|
||||||
comments: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
showInteraction: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
collectsum: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
likesum: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const common = useCommonStore()
|
const common = useCommonStore()
|
||||||
|
|
||||||
|
const comments = reactive({
|
||||||
|
list: [],
|
||||||
|
length: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
postid: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 处理评论点击事件
|
// 处理评论点击事件
|
||||||
function onDelegateTap(e) {
|
function onDelegateTap(e) {
|
||||||
const cid = e.target?.dataset?.cid
|
const cid = e.target?.dataset?.cid
|
||||||
if (!cid || !props.comments) return
|
if (!cid || !comments.list) return
|
||||||
const item = props.comments.find(v => v.id == cid)
|
const item = comments.list.find(v => v.id == cid)
|
||||||
if (!item || !item.children || !item.children.length) return
|
if (!item || !item.reply || !item.reply.length) return
|
||||||
item.showChild = !item.showChild
|
item.showChild = !item.showChild
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,31 +139,146 @@ function formatCount(count) {
|
|||||||
return common.formatCount(count)
|
return common.formatCount(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理评论数据,添加渲染节点
|
onLoad(() => {
|
||||||
const highlightedList = computed(() =>
|
const params = {
|
||||||
props.comments ? props.comments.map(c => ({
|
postId: props.postid
|
||||||
...c,
|
}
|
||||||
renderNodes: [
|
getCommentList(params).then(res => {
|
||||||
...common.atUsersToNodes(c.atUsers),
|
try {
|
||||||
{ type: 'text', text: c.content }
|
// 检查响应状态
|
||||||
],
|
if (res.statusCode === 200 && res.data) {
|
||||||
children: c.children ? c.children.map(child => ({
|
console.log(res.data)
|
||||||
...child,
|
comments.list = res.data.list || []
|
||||||
renderNodes: [
|
comments.length = res.data.total || 0
|
||||||
...common.atUsersToNodes(child.atUsers),
|
|
||||||
{ type: 'text', text: child.content }
|
// 处理每条评论
|
||||||
]
|
comments.list.forEach(comment => {
|
||||||
})) : []
|
processComment(comment)
|
||||||
})) : []
|
})
|
||||||
)
|
} else {
|
||||||
|
throw new Error(`请求失败: ${res.statusCode}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理评论数据时出错:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载评论失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
// 设置默认空数据
|
||||||
|
comments.list = []
|
||||||
|
comments.length = 0
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取评论列表失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '网络连接异常',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
// 设置默认空数据
|
||||||
|
comments.list = []
|
||||||
|
comments.length = 0
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理单条评论数据
|
||||||
|
* @param {Object} comment - 评论对象
|
||||||
|
*/
|
||||||
|
function processComment(comment) {
|
||||||
|
// 确保comment对象有必要的属性
|
||||||
|
if (!comment.children || comment.children.length === 0) {
|
||||||
|
comment.showChild = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保user对象存在
|
||||||
|
if (!comment.user) {
|
||||||
|
comment.user = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置评论基本属性
|
||||||
|
comment.content = comment.content || ''
|
||||||
|
comment.likeCount = comment.likeCount || 0
|
||||||
|
comment.userName = comment.user.nickName || '匿名用户'
|
||||||
|
comment.createdAt = comment.createdAt || comment.date || ''
|
||||||
|
|
||||||
|
// 生成renderNodes用于富文本渲染
|
||||||
|
comment.renderNodes = common.contentToRenderNodes(comment.content, comment.atUsers)
|
||||||
|
|
||||||
|
// 处理用户头像
|
||||||
|
handleCommentAvatar(comment)
|
||||||
|
|
||||||
|
// 处理子评论
|
||||||
|
if (comment.reply && comment.reply.length > 0) {
|
||||||
|
// 将reply赋值给children,确保模板和代码一致
|
||||||
|
comment.children = comment.reply
|
||||||
|
|
||||||
|
comment.children.forEach(childComment => {
|
||||||
|
processChildComment(childComment)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理子评论数据
|
||||||
|
* @param {Object} childComment - 子评论对象
|
||||||
|
*/
|
||||||
|
function processChildComment(childComment) {
|
||||||
|
// 确保user对象存在
|
||||||
|
if (!childComment.user) {
|
||||||
|
childComment.user = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置子评论基本属性
|
||||||
|
childComment.content = childComment.content || ''
|
||||||
|
childComment.likeCount = childComment.likeCount || 0
|
||||||
|
childComment.userName = childComment.user.nickName || '匿名用户'
|
||||||
|
childComment.date = childComment.createdAt || childComment.date || ''
|
||||||
|
|
||||||
|
// 处理子评论头像
|
||||||
|
handleCommentAvatar(childComment)
|
||||||
|
|
||||||
|
// 为子评论生成renderNodes
|
||||||
|
childComment.renderNodes = common.contentToRenderNodes(childComment.content, childComment.atUsers)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理评论头像
|
||||||
|
* @param {Object} comment - 评论对象(主评论或子评论)
|
||||||
|
*/
|
||||||
|
function handleCommentAvatar(comment) {
|
||||||
|
// 如果已经有头像URL,直接使用
|
||||||
|
if (comment.userAvatar) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保有头像路径时才请求用户信息
|
||||||
|
const avatarPath = comment.user.avatar
|
||||||
|
if (avatarPath) {
|
||||||
|
getUserImg(avatarPath).then(res => {
|
||||||
|
try {
|
||||||
|
if (res.statusCode === 200 && res.data) {
|
||||||
|
// 将arrayBuffer转换为base64
|
||||||
|
const base64 = uni.arrayBufferToBase64(res.data)
|
||||||
|
const userImgUrl = 'data:image/webp;base64,' + base64
|
||||||
|
comment.userAvatar = userImgUrl
|
||||||
|
} else {
|
||||||
|
// 使用原始头像URL作为备选
|
||||||
|
comment.userAvatar = avatarPath || ''
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('处理用户头像信息失败:', error)
|
||||||
|
comment.userAvatar = avatarPath || ''
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.warn('获取用户头像失败:', error)
|
||||||
|
comment.userAvatar = avatarPath || ''
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 没有头像路径时使用空字符串
|
||||||
|
comment.userAvatar = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 总评论数 = 主评论条数 + 所有子评论条数
|
|
||||||
const totalCommentCount = computed(() =>
|
|
||||||
props.comments ? props.comments.reduce(
|
|
||||||
(sum, c) => sum + 1 + (c.children ? c.children.length : 0),
|
|
||||||
0
|
|
||||||
) : 0
|
|
||||||
)
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -175,11 +292,16 @@ const totalCommentCount = computed(() =>
|
|||||||
.comment {
|
.comment {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 430px;
|
max-width: 430px;
|
||||||
padding: 16px;
|
height: 50vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commenthead {
|
.commenthead {
|
||||||
@@ -354,13 +476,12 @@ const totalCommentCount = computed(() =>
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 14px 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.commentchild {
|
.commentchild {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
margin-bottom: 8px;
|
/* margin-bottom: 8px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.commentchildleft {
|
.commentchildleft {
|
||||||
@@ -388,6 +509,8 @@ const totalCommentCount = computed(() =>
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expandcommenttext {
|
.expandcommenttext {
|
||||||
@@ -402,83 +525,25 @@ const totalCommentCount = computed(() =>
|
|||||||
color: #110c13;
|
color: #110c13;
|
||||||
}
|
}
|
||||||
|
|
||||||
.interaction {
|
.nocomments {
|
||||||
width: 100%;
|
|
||||||
max-width: 430px;
|
|
||||||
border: solid 1px #faf9fb;
|
|
||||||
background-color: #faf9fb;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
padding: 11.5px 16px;
|
text-align: center;
|
||||||
flex-direction: row;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
max-width: 100%;
|
||||||
|
|
||||||
.editarea {
|
|
||||||
flex: 1;
|
|
||||||
height: 40px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 24px;
|
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 0 20px;
|
padding: 12px;
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.editicon {
|
.nocommentstext {
|
||||||
width: 13.3px;
|
|
||||||
height: 14.6px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edittext {
|
|
||||||
flex: 1;
|
|
||||||
flex-grow: 0;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
font-family: 'PingFangSC';
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-stretch: normal;
|
font-stretch: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
line-height: 40px;
|
|
||||||
letter-spacing: normal;
|
|
||||||
text-align: left;
|
|
||||||
color: #918e93;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collection,
|
|
||||||
.like {
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collectionicon,
|
|
||||||
.likeicon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collectioncount,
|
|
||||||
.likecount {
|
|
||||||
height: 17px;
|
|
||||||
font-family: 'SFPro';
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
font-stretch: normal;
|
|
||||||
font-style: normal;
|
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
letter-spacing: normal;
|
letter-spacing: normal;
|
||||||
text-align: left;
|
text-align: center;
|
||||||
color: #000;
|
color: #918e93;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
141
src/pages/intereact/intereact.vue
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 互动区域 -->
|
||||||
|
<view class="interaction">
|
||||||
|
<view class="editarea" @tap="handleOpenApp">
|
||||||
|
<image src="/static/imgs/editicon/icon@2x.webp" mode="aspectFit" class="editicon" alt="编辑标签"></image>
|
||||||
|
<text class="edittext">快来互动吧…</text>
|
||||||
|
</view>
|
||||||
|
<view class="spacerview"></view>
|
||||||
|
<view class="collection" @tap="handleOpenApp">
|
||||||
|
<image src="@/static/imgs/staricon/icon@3x.webp" mode="aspectFit" class="collectionicon" alt="收藏标签"></image>
|
||||||
|
<text class="collectioncount">{{ formatCount(collectsum) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="spacerview"></view>
|
||||||
|
<view class="like" @tap="handleOpenApp">
|
||||||
|
<image src="@/static/imgs/likeicon/icon@3x.webp" mode="aspectFit" class="likeicon" alt="点赞标签"></image>
|
||||||
|
<text class="likecount">{{ formatCount(countLike) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useCommonStore } from '@/stores/common.js'
|
||||||
|
const props = defineProps({
|
||||||
|
countLike: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
collectsum: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const common = useCommonStore()
|
||||||
|
|
||||||
|
function handleOpenApp() {
|
||||||
|
common.openapp()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化数字显示
|
||||||
|
function formatCount(count) {
|
||||||
|
return common.formatCount(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.spacerview {
|
||||||
|
flex: 1;
|
||||||
|
pointer-events: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 430px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: auto auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interaction {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 430px;
|
||||||
|
border: solid 1px #faf9fb;
|
||||||
|
background-color: #faf9fb;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 11.5px 16px;
|
||||||
|
flex-direction: row;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: auto auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editarea {
|
||||||
|
flex: 1;
|
||||||
|
height: 40px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 24px;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editicon {
|
||||||
|
width: 13.3px;
|
||||||
|
height: 14.6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edittext {
|
||||||
|
flex: 1;
|
||||||
|
flex-grow: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: normal;
|
||||||
|
font-stretch: normal;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: 40px;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-align: left;
|
||||||
|
color: #918e93;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collection,
|
||||||
|
.like {
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collectionicon,
|
||||||
|
.likeicon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collectioncount,
|
||||||
|
.likecount {
|
||||||
|
height: 17px;
|
||||||
|
font-family: 'SFPro';
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-stretch: normal;
|
||||||
|
font-style: normal;
|
||||||
|
line-height: normal;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-align: left;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,22 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page">
|
<view v-if="loading" class="page">
|
||||||
|
|
||||||
<Head></Head>
|
<Head></Head>
|
||||||
<!-- 内容区 -->
|
<!-- 内容区 -->
|
||||||
<view class="content">
|
<main class="content">
|
||||||
<!-- 新闻区域 -->
|
<!-- 新闻区域 -->
|
||||||
<view class="news">
|
<section class="news">
|
||||||
|
|
||||||
<!-- 轮播图 -->
|
<!-- 轮播图 -->
|
||||||
<swiper v-if="post.imgs && post.imgs.length" :indicator-dots="false" :autoplay="false" :interval="3000"
|
<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">
|
:duration="1000" :circular="true" class="swiper-banner" @change="onChange">
|
||||||
<swiper-item v-for="(item, i) in post.imgs" :key="i">
|
<swiper-item v-for="(item, i) in post.imgs" :key="i">
|
||||||
<view class="swiper-center">
|
<view class="swiper-center">
|
||||||
<image v-if="item.type === 'img'" :src="item.src" class="swiper-item" mode="aspectFit" alt="新闻图片" />
|
<image v-if="item.type === 'img'" :src="item.src" class="swiper-item" mode="aspectFit" alt="新闻图片"
|
||||||
<video v-else :src="item.src" class="swiper-item" object-fit="contain" muted style="pointer-events:none;"
|
@load="onImageLoad(i)" @error="onImageError(i)" />
|
||||||
alt="新闻视频" />
|
<video v-else-if="item.type === 'video'" :src="item.src" class="swiper-item" object-fit="contain" muted
|
||||||
|
style="pointer-events:none;" alt="新闻视频" />
|
||||||
<cover-view class="swiper-mask" @tap="common.openapp">
|
<cover-view class="swiper-mask" @tap="common.openapp">
|
||||||
</cover-view>
|
</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>
|
</view>
|
||||||
</swiper-item>
|
</swiper-item>
|
||||||
</swiper>
|
</swiper>
|
||||||
@@ -31,16 +39,14 @@
|
|||||||
<text class="title">{{ post.title }}</text>
|
<text class="title">{{ post.title }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 底部文案 -->
|
<!-- 新闻信息 -->
|
||||||
<view v-if="post.copywriting && post.date" class="newsbottom">
|
<view v-if="post.copywriting && post.date" class="newsbottom">
|
||||||
<text class="copywriting">{{ post.copywriting }}</text>
|
<text class="copywriting">{{ post.copywriting }}</text>
|
||||||
<view class="meta-info">
|
<view class="meta-info">
|
||||||
<text v-if="post.source" class="source">{{ post.source }}</text>
|
<text v-if="post.source" class="source">{{ post.source }}</text>
|
||||||
<text v-if="post.source" class="date-text">·</text>
|
<text v-if="post.source" class="date-text">·</text>
|
||||||
<view class="datetextview">
|
<view class="datetextview">
|
||||||
<uni-dateformat :date="Date.parse(post.date.replace(/-/g, '/'))" :threshold="[0, 0]" format="yyyy-MM-dd"
|
<text class="date-text">{{ common.formatDate(post.time) }}</text>
|
||||||
class="date-text" />
|
|
||||||
<text class="date-text">{{ post.time }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="spacerview"></view>
|
<view class="spacerview"></view>
|
||||||
<view class="toseeall" @tap="handleInteraction">
|
<view class="toseeall" @tap="handleInteraction">
|
||||||
@@ -51,16 +57,26 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</section>
|
||||||
|
|
||||||
</view>
|
</main>
|
||||||
|
|
||||||
<!-- 评论区域 + 互动区域 -->
|
<!-- 评论区域 + 互动区域 -->
|
||||||
<Comments :comments="post.comments" :showInteraction="true" :collectsum="post.collectsum" :likesum="post.likesum" />
|
<Comments :postid="post.id" />
|
||||||
|
|
||||||
|
<!-- 互动区域 -->
|
||||||
|
<!-- <Intereact :countLike="post.countLike" :collectsum="post.collectsum" /> -->
|
||||||
|
|
||||||
<!-- Findmore -->
|
<!-- Findmore -->
|
||||||
<Findmore />
|
<Findmore />
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 错误显示 -->
|
||||||
|
<!-- <view v-else class="errorload">
|
||||||
|
<image src="/static/imgs/errorload/errorload@3x.webp" mode="aspectFit" alt="加载失败"></image>
|
||||||
|
</view> -->
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -70,6 +86,9 @@ import { useCommonStore } from '@/stores/common.js'
|
|||||||
import Head from '@/pages/head/head.vue'
|
import Head from '@/pages/head/head.vue'
|
||||||
import Comments from '@/pages/comments/comments.vue'
|
import Comments from '@/pages/comments/comments.vue'
|
||||||
import Findmore from '@/pages/findmore/findmore.vue'
|
import Findmore from '@/pages/findmore/findmore.vue'
|
||||||
|
import { getPostList, getPostLImage, getPostVideo } from '../api/api.js'
|
||||||
|
// import Intereact from '@/pages/intereact/intereact.vue'
|
||||||
|
|
||||||
const common = useCommonStore()
|
const common = useCommonStore()
|
||||||
|
|
||||||
// 当前 dot
|
// 当前 dot
|
||||||
@@ -78,108 +97,136 @@ const onChange = e => current.value = e.detail.current
|
|||||||
|
|
||||||
// 动态数据
|
// 动态数据
|
||||||
const post = ref({})
|
const post = ref({})
|
||||||
|
// 资源加载状态
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
// 模拟获取数据函数
|
// 图片加载成功处理
|
||||||
const fetchPostData = async () => {
|
const onImageLoad = (index) => {
|
||||||
try {
|
if (post.value.imgs && post.value.imgs[index]) {
|
||||||
// 使用uni.request替代axios
|
post.value.imgs[index].loading = false
|
||||||
const response = await new Promise((resolve, reject) => {
|
post.value.imgs[index].error = false
|
||||||
uni.request({
|
}
|
||||||
url: '', // 这里可以填写实际API地址
|
}
|
||||||
method: 'GET',
|
|
||||||
timeout: 1000,
|
// 图片加载失败处理
|
||||||
success: resolve,
|
const onImageError = (index) => {
|
||||||
fail: reject
|
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 mockData = {
|
|
||||||
userId: 1000011,
|
|
||||||
userName: 'Kun Chang-Min',
|
|
||||||
userImg: '/static/logo.png',
|
|
||||||
date: '2024-12-03',
|
|
||||||
time: '14:00',
|
|
||||||
title: '智能体是人类智慧的延伸,它们将成为我们最强大的工具,也是最亲密的朋友。',
|
|
||||||
source: '36氪',
|
|
||||||
copywriting: '智能体是人类智慧的延伸,它们将成为我们最强大的工具,也是最亲密的朋友。',
|
|
||||||
collectsum: 114514,
|
|
||||||
likesum: 114514,
|
|
||||||
imgs: [
|
|
||||||
{ type: 'video', src: '/static/videos/beauty.mp4' }
|
|
||||||
],
|
|
||||||
comments: [
|
|
||||||
{
|
|
||||||
id: 101,
|
|
||||||
userName: 'Brad Lewin',
|
|
||||||
atUsers: ['JackyLove'],
|
|
||||||
content: '今天天气真不错~',
|
|
||||||
date: '2024-12-01',
|
|
||||||
likeCount: 99999,
|
|
||||||
showChild: false,
|
|
||||||
children: [
|
|
||||||
{ id: 201, userName: 'Alice', content: '快智能体是人类智慧的延伸,它们将成为我们最强大的工具,也是最亲密的朋友。智能体是人类智慧的延伸', atUsers: ['Brad Lewin', 'JackyLove'], date: '2024-12-01', likeCount: 2 },
|
|
||||||
{ id: 202, userName: 'Bob', content: '我同意你的观点。我记得喜欢这个版本。这个新版本对我来说Nothing好处', atUsers: [], date: '2024-12-01', likeCount: 1 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 102,
|
|
||||||
userName: 'Leanne Simpson',
|
|
||||||
atUsers: ['Theshy'],
|
|
||||||
content: '有人一起开黑吗?',
|
|
||||||
date: '2024-12-02',
|
|
||||||
likeCount: 1145140,
|
|
||||||
showChild: false,
|
|
||||||
children: [
|
|
||||||
{ id: 203, userName: 'Carol', content: '带我一个', atUsers: [], date: '2024-12-02', likeCount: 3 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 103,
|
|
||||||
userName: 'Tom Hardy',
|
|
||||||
atUsers: [],
|
|
||||||
content: '主评论3:分享一张今晚的月亮。',
|
|
||||||
date: '2024-12-03',
|
|
||||||
likeCount: 999,
|
|
||||||
showChild: false,
|
|
||||||
children: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将模拟数据赋值给post
|
|
||||||
post.value = mockData
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取数据失败:', error)
|
|
||||||
// 如果获取失败,使用默认数据
|
|
||||||
post.value = {
|
|
||||||
userId: 1000011,
|
|
||||||
userName: 'Kun Chang-Min',
|
|
||||||
userImg: '/static/logo.png',
|
|
||||||
date: '2024-12-03',
|
|
||||||
title: '人工智能技术的最新突破',
|
|
||||||
source: '科技日报',
|
|
||||||
copywriting: '智能体是人类智慧的延伸,它们将成为我们最强大的工具,也是最亲密的朋友。',
|
|
||||||
collectsum: 114514,
|
|
||||||
likesum: 114514,
|
|
||||||
imgs: [
|
|
||||||
{ type: 'img', src: '/static/logo.png' }
|
|
||||||
],
|
|
||||||
comments: []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理点击查看全文
|
|
||||||
const handleInteraction = () => {
|
const handleInteraction = () => {
|
||||||
common.openapp();
|
common.openapp();
|
||||||
}
|
}
|
||||||
// 组件挂载时获取数据
|
// 组件挂载时获取数据
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
fetchPostData()
|
const params = {
|
||||||
|
newsFilter: 'news_only'
|
||||||
|
}
|
||||||
|
|
||||||
|
getPostList(params).then(res => {
|
||||||
|
try {
|
||||||
|
const data = res.data.data
|
||||||
|
// console.log(data)
|
||||||
|
|
||||||
|
// 处理新闻相关字段 - 根据实际API返回字段优化
|
||||||
|
data.id = data.id || ''
|
||||||
|
data.title = data.newsTitle || data.title || ''
|
||||||
|
data.source = data.newsSource || data.source || ''
|
||||||
|
data.copywriting = data.newsContent || data.textContent || data.copywriting || ''
|
||||||
|
data.date = data.time || data.date || ''
|
||||||
|
data.countLike = data.likeCount || data.countLike || 0
|
||||||
|
data.collectsum = data.collectsum || 0
|
||||||
|
data.comments = data.comments || []
|
||||||
|
|
||||||
|
const mediaPromises = []
|
||||||
|
|
||||||
|
// 处理图片资源
|
||||||
|
if (data.images && Array.isArray(data.images)) {
|
||||||
|
data.images.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 => {
|
||||||
|
console.warn('图片资源加载失败:', error)
|
||||||
|
return null
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//处理视频资源
|
||||||
|
if (data.video && data.video.original_url) {
|
||||||
|
mediaPromises.push(getPostVideo(data.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: data.video.original_url,
|
||||||
|
loading: true, // 初始加载状态
|
||||||
|
error: false // 错误状态
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}).catch(error => {
|
||||||
|
console.warn('视频资源加载失败:', error)
|
||||||
|
return null
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一等待所有媒体资源加载完成
|
||||||
|
if (mediaPromises.length > 0) {
|
||||||
|
Promise.all(mediaPromises).then(mediaItems => {
|
||||||
|
// 过滤掉null值,将有效的媒体资源合并到imgs数组中
|
||||||
|
data.imgs = mediaItems.filter(item => item !== null)
|
||||||
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -194,7 +241,7 @@ page,
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
main {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -320,9 +367,36 @@ swiper-item {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 图片加载状态样式 */
|
||||||
|
.image-loading,
|
||||||
|
.image-error {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
color: white;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-error {
|
||||||
|
background: rgba(255, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 200px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
.spacerview {
|
.spacerview {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
|
||||||
pointer-events: none
|
pointer-events: none
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,4 +419,19 @@ swiper-item {
|
|||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.errorload {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorload image {
|
||||||
|
max-width: 80%;
|
||||||
|
max-height: 80%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page">
|
<view v-if="loading" class="page">
|
||||||
|
|
||||||
<Head></Head>
|
<Head></Head>
|
||||||
<!-- 内容区 -->
|
<!-- 内容区 -->
|
||||||
@@ -8,8 +8,10 @@
|
|||||||
<view class="moment">
|
<view class="moment">
|
||||||
<!-- 用户栏 -->
|
<!-- 用户栏 -->
|
||||||
<view class="userbar">
|
<view class="userbar">
|
||||||
<image :src="post.userImg" mode="aspectFill" class="userimg" alt="用户头像" />
|
<image v-if="post.user && post.user.userImg" :src="post.user.userImg" mode="aspectFill" class="userimg"
|
||||||
<text class="username">{{ post.userName }}</text>
|
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">
|
<button class="follow" @tap="common.openapp">
|
||||||
<uni-icons v-if="post.isfollow" type="checkmarkempty" size="20" color="#333"></uni-icons>
|
<uni-icons v-if="post.isfollow" type="checkmarkempty" size="20" color="#333"></uni-icons>
|
||||||
<text v-else>关注</text>
|
<text v-else>关注</text>
|
||||||
@@ -17,15 +19,28 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 轮播图 -->
|
<!-- 轮播图 -->
|
||||||
<swiper v-if="post.imgs && post.imgs.length" :indicator-dots="false" :autoplay="false" :interval="3000"
|
<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">
|
:duration="1000" :circular="true" class="swiper-banner" @change="onChange">
|
||||||
<swiper-item v-for="(item, i) in post.imgs" :key="i">
|
<swiper-item v-for="(item, i) in post.imgs" :key="i">
|
||||||
<view class="swiper-center">
|
<view class="swiper-center">
|
||||||
<image v-if="item.type === 'img'" :src="item.src" class="swiper-item" mode="aspectFit" alt="动态图片内容" />
|
<image v-if="item.type === 'img'" :src="item.src" class="swiper-item" mode="aspectFit" alt="动态图片内容"
|
||||||
<video v-else :src="item.src" class="swiper-item" object-fit="contain" muted style="pointer-events:none;"
|
@load="onImageLoad(i)" @error="onImageError(i)" />
|
||||||
alt="动态视频内容" />
|
<video v-else-if="item.type === 'video'" id="videoid" :src="item.src" class="swiper-item"
|
||||||
<cover-view class="swiper-mask" @tap="common.openapp">
|
:controls="false" @tap="pausevideo" object-fit="contain" alt="动态视频内容" />
|
||||||
</cover-view>
|
<!-- 播放按钮 - 使用浏览器复制的样式 -->
|
||||||
|
<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>
|
</view>
|
||||||
</swiper-item>
|
</swiper-item>
|
||||||
</swiper>
|
</swiper>
|
||||||
@@ -47,21 +62,30 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 评论区域 + 互动区域 -->
|
<!-- 评论区域 + 互动区域 -->
|
||||||
<Comments :comments="post.comments" :showInteraction="true" :collectsum="post.collectsum" :likesum="post.likesum" />
|
<Comments :postid="post.id" />
|
||||||
|
|
||||||
|
<!-- 互动区域 -->
|
||||||
|
<!-- <Intereact :collectsum="post.collectsum" :likesum="post.likesum" /> -->
|
||||||
|
|
||||||
<!-- Findmore -->
|
<!-- Findmore -->
|
||||||
<Findmore />
|
<Findmore />
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="loading-container">
|
||||||
|
<text>页面加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref, getCurrentInstance } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import { useCommonStore } from '@/stores/common.js'
|
import { useCommonStore } from '@/stores/common.js'
|
||||||
import Head from '@/pages/head/head.vue'
|
import Head from '@/pages/head/head.vue'
|
||||||
import Comments from '@/pages/comments/comments.vue'
|
import Comments from '@/pages/comments/comments.vue'
|
||||||
import Findmore from '@/pages/findmore/findmore.vue'
|
import Findmore from '@/pages/findmore/findmore.vue'
|
||||||
|
import { getPostList, getPostLImage, getPostVideo, getUserImg } from '../api/api.js'
|
||||||
const common = useCommonStore()
|
const common = useCommonStore()
|
||||||
|
|
||||||
// 当前 dot
|
// 当前 dot
|
||||||
@@ -70,102 +94,203 @@ const onChange = e => current.value = e.detail.current
|
|||||||
|
|
||||||
// 动态数据
|
// 动态数据
|
||||||
const post = ref({})
|
const post = ref({})
|
||||||
|
// 播放状态
|
||||||
|
const isPlaying = ref(true) // 默认播放状态
|
||||||
|
// 资源加载状态
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
// 模拟获取数据函数
|
// 图片加载成功处理
|
||||||
const fetchPostData = async () => {
|
const onImageLoad = (index) => {
|
||||||
try {
|
if (post.value.imgs && post.value.imgs[index]) {
|
||||||
// 使用uni.request替代axios
|
post.value.imgs[index].loading = false
|
||||||
const response = await new Promise((resolve, reject) => {
|
post.value.imgs[index].error = false
|
||||||
uni.request({
|
|
||||||
url: '', // 这里可以填写实际API地址
|
|
||||||
method: 'GET',
|
|
||||||
timeout: 1000,
|
|
||||||
success: resolve,
|
|
||||||
fail: reject
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// 处理数据...
|
|
||||||
const mockData = {
|
|
||||||
userId: 1000011,
|
|
||||||
userName: 'Kun Chang-Min',
|
|
||||||
userImg: '/static/logo.png',
|
|
||||||
date: '2024-12-03',
|
|
||||||
copywriting: '智能体是人类智慧的延伸,它们将成为我们最强大的工具,也是最亲密的朋友。',
|
|
||||||
isfollow: false,
|
|
||||||
collectsum: 114514,
|
|
||||||
likesum: 114514,
|
|
||||||
imgs: [
|
|
||||||
{ type: 'img', src: '/static/logo.png' },
|
|
||||||
{ type: 'video', src: '/static/videos/beauty.mp4' },
|
|
||||||
{ type: 'img', src: '/static/imgs/comment.webp' },
|
|
||||||
{ type: 'img', src: '/static/imgs/image-138.webp' }
|
|
||||||
],
|
|
||||||
comments: [
|
|
||||||
{
|
|
||||||
id: 101,
|
|
||||||
userName: 'Brad Lewin',
|
|
||||||
atUsers: ['JackyLove'],
|
|
||||||
content: '今天天气真不错~',
|
|
||||||
date: '2024-12-01',
|
|
||||||
likeCount: 99999,
|
|
||||||
showChild: false,
|
|
||||||
children: [
|
|
||||||
{ id: 201, userName: 'Alice', content: '快智能体是人类智慧的延伸,它们将成为我们最强大的工具,也是最亲密的朋友。智能体是人类智慧的延伸', atUsers: ['Brad Lewin', 'JackyLove'], date: '2024-12-01', likeCount: 2 },
|
|
||||||
{ id: 202, userName: 'Bob', content: '我同意你的观点。我记得喜欢这个版本。这个新版本对我来说Nothing好处', atUsers: [], date: '2024-12-01', likeCount: 1 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 102,
|
|
||||||
userName: 'Leanne Simpson',
|
|
||||||
atUsers: ['Theshy'],
|
|
||||||
content: '有人一起开黑吗?',
|
|
||||||
date: '2024-12-02',
|
|
||||||
likeCount: 1145140,
|
|
||||||
showChild: false,
|
|
||||||
children: [
|
|
||||||
{ id: 203, userName: 'Carol', content: '带我一个', atUsers: [], date: '2024-12-02', likeCount: 3 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 103,
|
|
||||||
userName: 'Tom Hardy',
|
|
||||||
atUsers: [],
|
|
||||||
content: '主评论3:分享一张今晚的月亮。',
|
|
||||||
date: '2024-12-03',
|
|
||||||
likeCount: 999,
|
|
||||||
showChild: false,
|
|
||||||
children: []
|
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
|
|
||||||
|
// 图片加载失败处理
|
||||||
|
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}`)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 将模拟数据赋值给post
|
//暂停/播放
|
||||||
post.value = mockData
|
const pausevideo = () => {
|
||||||
|
// 获取VideoContext实例
|
||||||
} catch (error) {
|
const videoCtx = uni.createVideoContext('videoid', getCurrentInstance());
|
||||||
console.error('获取数据失败:', error)
|
// 检查视频状态并切换播放/暂停
|
||||||
// 如果获取失败,使用默认数据
|
if (videoCtx) {
|
||||||
post.value = {
|
// 使用一个状态变量来跟踪播放状态
|
||||||
userId: 1000011,
|
if (isPlaying.value) {
|
||||||
userName: 'Kun Chang-Min',
|
videoCtx.pause()
|
||||||
userImg: '/static/logo.png',
|
isPlaying.value = false
|
||||||
date: '2024-12-03',
|
} else {
|
||||||
copywriting: '智能体是人类智慧的延伸,它们将成为我们最强大的工具,也是最亲密的朋友。',
|
videoCtx.play()
|
||||||
isfollow: false,
|
isPlaying.value = true
|
||||||
collectsum: 114514,
|
|
||||||
likesum: 114514,
|
|
||||||
imgs: [
|
|
||||||
{ type: 'img', src: '/static/logo.png' }
|
|
||||||
],
|
|
||||||
comments: []
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 组件挂载时获取数据
|
// 组件挂载时获取数据
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
fetchPostData()
|
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>
|
</script>
|
||||||
@@ -253,7 +378,8 @@ swiper-item {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-item {
|
.swiper-item {
|
||||||
@@ -261,17 +387,63 @@ swiper-item {
|
|||||||
height: 100%
|
height: 100%
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-center {
|
::v-deep .uni-video-cover {
|
||||||
position: relative;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-mask {
|
::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;
|
position: absolute;
|
||||||
left: 0;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: transparent;
|
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 {
|
.dots-bar {
|
||||||
@@ -319,4 +491,11 @@ swiper-item {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #b1aeb2;
|
color: #b1aeb2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,31 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="videopage">
|
<main>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 视频容器 -->
|
||||||
|
<section class="video-container">
|
||||||
|
|
||||||
<Head></Head>
|
<Head></Head>
|
||||||
<video id="videoid" ref="videoRef" :src="videoData.videoUrl" :danmu-list="videoData.danmuList" enable-danmu loop
|
|
||||||
:muted="isMuted" :show-mute-btn="true" :controls="false" object-fit="contain" @tap="pausevideo"></video>
|
|
||||||
|
|
||||||
<!-- 播放按钮 - 使用浏览器复制的样式 -->
|
<video id="videoid" :src="videoData.videoUrl" :danmu-list="videoData.danmuList" enable-danmu loop :muted="isMuted"
|
||||||
<view v-if="!isPlaying" class="uni-video-cover" @tap="pausevideo">
|
:show-mute-btn="true" :controls="false" object-fit="contain" @tap="pausevideo">
|
||||||
|
|
||||||
|
<!-- 播放按钮 - 直接使用浏览器复制的样式 -->
|
||||||
|
<section v-if="!isPlaying" class="uni-video-cover" @tap="pausevideo">
|
||||||
<view class="uni-video-cover-play-button uni-video-icon"></view>
|
<view class="uni-video-cover-play-button uni-video-icon"></view>
|
||||||
</view>
|
</section>
|
||||||
|
|
||||||
<!-- 视频信息 -->
|
|
||||||
<view class="videoinfo">
|
|
||||||
<view class="vedioinfohead">
|
|
||||||
<text class="username">@{{ videoData.userName }}</text>
|
|
||||||
<text class="datetime">{{ formatDate(videoData.date) }}</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="content-container">
|
</video>
|
||||||
<text class="content" :class="{ 'expanded': isExpanded }">{{ videoData.copywriting }}</text>
|
|
||||||
<view class="flodbtncontainer">
|
|
||||||
<image v-if="videoData.copywriting.length > 30" class="expand-btn" :class="{ 'rotated': isExpanded }"
|
|
||||||
:src="isExpanded ? '/static/imgs/foldicon/flodicon@3x.webp' : '/static/imgs/foldicon/flodicon@3x.webp'"
|
|
||||||
mode="aspectFit" @tap="toggleExpand"></image>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 右侧交互按钮 -->
|
<!-- 右侧交互按钮 -->
|
||||||
<view class="interaction-panel">
|
<view class="interaction-panel">
|
||||||
@@ -33,7 +24,7 @@
|
|||||||
<view class="user-section">
|
<view class="user-section">
|
||||||
<image :src="videoData.userImg" class="user-avatar" mode="aspectFill"></image>
|
<image :src="videoData.userImg" class="user-avatar" mode="aspectFill"></image>
|
||||||
<view class="follow-btn-container" @tap="() => handleInteraction('follow')">
|
<view class="follow-btn-container" @tap="() => handleInteraction('follow')">
|
||||||
<image src="/static/imgs/followbtn/btn@3x.webp" mode="aspectFit"></image>
|
<image src="/static/imgs/followbtn/btn@3x.webp" mode="aspectFit" style="height: 24px;"></image>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -60,132 +51,102 @@
|
|||||||
class="mutebtn" mode="aspectFit" @tap="toggleMute"></image>
|
class="mutebtn" mode="aspectFit" @tap="toggleMute"></image>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 视频信息 -->
|
||||||
|
<section class="videoinfo">
|
||||||
|
<view class="vedioinfohead" @tap="() => handleInteraction('user')">
|
||||||
|
<text class="username">{{ '@' + videoData.userName }}</text>
|
||||||
|
<time class="datetime">{{ formatDate(videoData.date) }}</time>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view class="content-container">
|
||||||
|
<text class="content" :class="{ 'expanded': isExpanded }">{{ isExpanded ? videoData.copywriting :
|
||||||
|
truncatedText }}</text>
|
||||||
|
<view class="flodbtncontainer">
|
||||||
|
<image v-if="showExpandButton" class="expand-btn" :class="{ 'rotated': isExpanded }"
|
||||||
|
:src="isExpanded ? '/static/imgs/foldicon/flodicon@3x.webp' : '/static/imgs/expandicon/expandicon@3x.webp'"
|
||||||
|
mode="aspectFit" @tap="toggleExpand"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<!-- 评论区域 + 互动区域 -->
|
<!-- 评论区域 + 互动区域 -->
|
||||||
<Comments :comments="videoData.comments" :showInteraction="true" :collectsum="videoData.collectsum"
|
<Comments :postid="videoData.id" />
|
||||||
:likesum="videoData.likesum" />
|
|
||||||
|
|
||||||
|
<view class="spacerview"></view>
|
||||||
|
|
||||||
|
<!-- 互动区域 -->
|
||||||
|
<!-- <Intereact /> -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</main>
|
||||||
<!-- Findmore -->
|
<!-- Findmore -->
|
||||||
<Findmore />
|
<!-- <Findmore /> -->
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, getCurrentInstance } from 'vue'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
import { ref, reactive, getCurrentInstance, computed } from 'vue'
|
||||||
import { useCommonStore } from '@/stores/common.js'
|
import { useCommonStore } from '@/stores/common.js'
|
||||||
import Head from '@/pages/head/head.vue'
|
import Head from '@/pages/head/head.vue'
|
||||||
import Comments from '@/pages/comments/comments.vue'
|
import Comments from '@/pages/comments/comments.vue'
|
||||||
import Findmore from '@/pages/findmore/findmore.vue'
|
import Intereact from '@/pages/intereact/intereact.vue'
|
||||||
|
import { getPostList, getPostVideo, getUserImg } from '@/api/api.js'
|
||||||
|
|
||||||
const common = useCommonStore()
|
const common = useCommonStore()
|
||||||
const formatCount = common.formatCount
|
const formatCount = common.formatCount
|
||||||
const formatDate = common.formatDate
|
const formatDate = common.formatDate
|
||||||
|
|
||||||
|
const videoData = reactive({})
|
||||||
|
|
||||||
// 折叠展开状态
|
// 折叠展开状态
|
||||||
const isExpanded = ref(false)
|
const isExpanded = ref(false)
|
||||||
const videoRef = ref(null)
|
// const videoRef = ref(null)
|
||||||
// 静音状态
|
// 静音状态
|
||||||
const isMuted = ref(false)
|
const isMuted = ref(false)
|
||||||
// 播放状态
|
// 播放状态
|
||||||
const isPlaying = ref(true) // 默认播放状态
|
const isPlaying = ref(true) // 默认播放状态
|
||||||
|
|
||||||
// 视频数据(包含弹幕)
|
// 计算属性:是否需要显示展开按钮
|
||||||
const videoData = reactive({
|
const showExpandButton = computed(() => {
|
||||||
userId: 1000012,
|
if (!videoData.copywriting) return false
|
||||||
userName: '蔡徐坤',
|
// 考虑中文字符,每个中文字符算2个字符宽度
|
||||||
userImg: '/static/logo.png',
|
const textLength = videoData.copywriting.replace(/[^\x00-\xff]/g, '**').length
|
||||||
date: '2024-01-15',
|
return textLength > 30
|
||||||
copywriting: '鸡你太美!练习时长两年半的个人练习生!鸡你太美!练习时长两年半的个人练习生!鸡你太美!练习时长两年半的个人练习生!',
|
})
|
||||||
likesum: 1145140,
|
|
||||||
collectsum: 114514,
|
// 计算属性:截断后的文本
|
||||||
commentsum: 114514,
|
const truncatedText = computed(() => {
|
||||||
sharesum: 1145,
|
if (!videoData.copywriting) return ''
|
||||||
videoUrl: '/static/videos/beauty.mp4',
|
if (!showExpandButton.value) return videoData.copywriting
|
||||||
comments: [
|
|
||||||
{
|
// 智能截断,保留完整的中文字符
|
||||||
id: 101,
|
let result = ''
|
||||||
userName: 'Brad Lewin',
|
let charCount = 0
|
||||||
atUsers: ['JackyLove'],
|
|
||||||
content: '今天天气真不错~',
|
for (let i = 0; i < videoData.copywriting.length; i++) {
|
||||||
date: '2024-12-01',
|
const char = videoData.copywriting[i]
|
||||||
likeCount: 99999,
|
// 中文字符算2个字符,其他算1个
|
||||||
showChild: false,
|
charCount += /[^\x00-\xff]/.test(char) ? 2 : 1
|
||||||
children: [
|
|
||||||
{ id: 201, userName: 'Alice', content: '快智能体是人类智慧的延伸,它们将成为我们最强大的工具,也是最亲密的朋友。智能体是人类智慧的延伸', atUsers: ['Brad Lewin', 'JackyLove'], date: '2024-12-01', likeCount: 2 },
|
if (charCount <= 30) {
|
||||||
{ id: 202, userName: 'Bob', content: '我同意你的观点。我记得喜欢这个版本。这个新版本对我来说Nothing好处', atUsers: [], date: '2024-12-01', likeCount: 1 }
|
result += char
|
||||||
]
|
} else {
|
||||||
},
|
break
|
||||||
{
|
|
||||||
id: 102,
|
|
||||||
userName: 'Leanne Simpson',
|
|
||||||
atUsers: ['Theshy'],
|
|
||||||
content: '有人一起开黑吗?',
|
|
||||||
date: '2024-12-02',
|
|
||||||
likeCount: 1145140,
|
|
||||||
showChild: false,
|
|
||||||
children: [
|
|
||||||
{ id: 203, userName: 'Carol', content: '带我一个', atUsers: [], date: '2024-12-02', likeCount: 3 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 103,
|
|
||||||
userName: 'Tom Hardy',
|
|
||||||
atUsers: [],
|
|
||||||
content: '主评论3:分享一张今晚的月亮。',
|
|
||||||
date: '2024-12-03',
|
|
||||||
likeCount: 999,
|
|
||||||
showChild: false,
|
|
||||||
children: []
|
|
||||||
}
|
}
|
||||||
],
|
}
|
||||||
danmuList: [
|
|
||||||
{ text: '鸡你太美!', color: '#FFFFFF', time: 1 },
|
// 如果截断后的文本和原文本相同,不需要加省略号
|
||||||
{ text: '练习时长两年半', color: '#FF0000', time: 1 },
|
if (result === videoData.copywriting) {
|
||||||
{ text: '个人练习生', color: '#00FF00', time: 1 },
|
return result
|
||||||
{ text: '坤坤加油!', color: '#FFFF00', time: 3 },
|
}
|
||||||
{ text: '篮球高手', color: '#FFA500', time: 3 },
|
|
||||||
{ text: '坤坤跳舞真帅', color: '#FF69B4', time: 5 },
|
return result + '...'
|
||||||
{ text: '这舞步绝了', color: '#00FFFF', time: 5 },
|
|
||||||
{ text: '音乐太洗脑了', color: '#FFD700', time: 7 },
|
|
||||||
{ text: '坤坤我爱你', color: '#FF1493', time: 7 },
|
|
||||||
{ text: '练习生之光', color: '#7CFC00', time: 9 },
|
|
||||||
{ text: '这节奏太上头', color: '#FF6347', time: 9 },
|
|
||||||
{ text: '坤坤的招牌动作', color: '#9370DB', time: 11 },
|
|
||||||
{ text: '鸡你太美循环中', color: '#20B2AA', time: 11 },
|
|
||||||
{ text: '两年半的功力', color: '#FF4500', time: 13 },
|
|
||||||
{ text: '坤坤的舞姿', color: '#DA70D6', time: 13 },
|
|
||||||
{ text: '这歌太魔性了', color: '#32CD32', time: 15 },
|
|
||||||
{ text: '练习生天花板', color: '#FF8C00', time: 15 },
|
|
||||||
{ text: '坤坤的台风', color: '#00BFFF', time: 17 },
|
|
||||||
{ text: '鸡你太美经典', color: '#FF00FF', time: 17 },
|
|
||||||
{ text: '坤坤的舞台魅力', color: '#FFDAB9', time: 19 },
|
|
||||||
{ text: '这舞步太经典', color: '#98FB98', time: 19 },
|
|
||||||
{ text: '坤坤的招牌笑容', color: '#FFB6C1', time: 21 },
|
|
||||||
{ text: '练习生传奇', color: '#87CEFA', time: 21 },
|
|
||||||
{ text: '坤坤的舞蹈功底', color: '#FFA07A', time: 23 },
|
|
||||||
{ text: '坤坤太帅了', color: '#FF1493', time: 25 },
|
|
||||||
{ text: '这舞步太丝滑', color: '#00CED1', time: 25 },
|
|
||||||
{ text: '坤坤的节奏感', color: '#FF69B4', time: 27 },
|
|
||||||
{ text: '鸡你太美神曲', color: '#32CD32', time: 27 },
|
|
||||||
{ text: '坤坤的舞台表现', color: '#FF4500', time: 29 },
|
|
||||||
{ text: '这舞蹈太经典', color: '#9370DB', time: 29 },
|
|
||||||
{ text: '坤坤的粉丝来了', color: '#FFD700', time: 2 },
|
|
||||||
{ text: '鸡你太美循环播放', color: '#00BFFF', time: 4 },
|
|
||||||
{ text: '坤坤的舞蹈功底', color: '#FF6347', time: 6 },
|
|
||||||
{ text: '这歌太魔性了', color: '#20B2AA', time: 8 },
|
|
||||||
{ text: '坤坤的招牌动作', color: '#FF8C00', time: 10 },
|
|
||||||
{ text: '练习时长两年半', color: '#FF00FF', time: 12 },
|
|
||||||
{ text: '坤坤的舞台魅力', color: '#98FB98', time: 14 },
|
|
||||||
{ text: '鸡你太美经典', color: '#FFB6C1', time: 16 },
|
|
||||||
{ text: '坤坤的舞姿', color: '#87CEFA', time: 18 },
|
|
||||||
{ text: '这节奏太上头', color: '#FFA07A', time: 20 },
|
|
||||||
{ text: '坤坤的台风', color: '#FF1493', time: 22 },
|
|
||||||
{ text: '鸡你太美神曲', color: '#00CED1', time: 24 },
|
|
||||||
{ text: '坤坤的舞蹈功底', color: '#FF69B4', time: 26 },
|
|
||||||
{ text: '这舞步太丝滑', color: '#32CD32', time: 28 },
|
|
||||||
{ text: '坤坤太帅了', color: '#FF4500', time: 30 }
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 切换展开状态
|
// 切换展开状态
|
||||||
@@ -226,30 +187,163 @@ const toggleMute = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
const params = 'video_only'
|
||||||
|
getPostList(params).then(res => {
|
||||||
|
try {
|
||||||
|
// 检查响应状态
|
||||||
|
if (res.statusCode === 200 && res.data) {
|
||||||
|
const data = res.data.data
|
||||||
|
console.log('视频数据:', data)
|
||||||
|
|
||||||
|
// 创建Promise数组来处理异步资源
|
||||||
|
const resourcePromises = []
|
||||||
|
|
||||||
|
// 处理视频资源 - 直接取第一个视频
|
||||||
|
if (data.videos && Array.isArray(data.videos) && data.videos.length > 0) {
|
||||||
|
const video = data.videos[0] // 直接取第一个视频
|
||||||
|
resourcePromises.push(getPostVideo(video.original_url).then(videoRes => {
|
||||||
|
if (videoRes.statusCode === 200 && videoRes.data) {
|
||||||
|
// 使用uni.arrayBufferToBase64方法,参考post.vue的实现
|
||||||
|
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 => {
|
||||||
|
console.warn('获取视频失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '视频加载失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
return null
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理用户头像
|
||||||
|
if (data.user && data.user.avatar) {
|
||||||
|
resourcePromises.push(getUserImg(data.user.avatar).then(avatarRes => {
|
||||||
|
if (avatarRes.statusCode === 200 && avatarRes.data) {
|
||||||
|
// 将arrayBuffer转换为base64
|
||||||
|
const base64 = uni.arrayBufferToBase64(avatarRes.data)
|
||||||
|
const userImgUrl = 'data:image/webp;base64,' + base64
|
||||||
|
return {
|
||||||
|
type: 'avatar',
|
||||||
|
url: userImgUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}).catch(error => {
|
||||||
|
console.warn('获取用户头像失败:', error)
|
||||||
|
return null
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待所有资源加载完成
|
||||||
|
Promise.all(resourcePromises).then(results => {
|
||||||
|
// 处理视频结果
|
||||||
|
const videoResults = results.filter(result => result && result.type === 'video')
|
||||||
|
if (videoResults.length > 0) {
|
||||||
|
// 使用第一个视频作为主视频
|
||||||
|
const mainVideo = videoResults[0]
|
||||||
|
videoData.videoUrl = mainVideo.src
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理头像结果
|
||||||
|
const avatarResult = results.find(result => result && result.type === 'avatar')
|
||||||
|
if (avatarResult) {
|
||||||
|
videoData.userImg = avatarResult.url
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新视频数据 - 沿用data对象
|
||||||
|
data.userName = data.user?.nickName || '匿名用户'
|
||||||
|
data.date = data.time || data.date || ''
|
||||||
|
data.copywriting = data.textContent || ''
|
||||||
|
data.likesum = data.likeCount || 0
|
||||||
|
data.commentsum = data.commentCount || 0
|
||||||
|
data.sharesum = data.shareCount || 0
|
||||||
|
|
||||||
|
// 最后用data覆盖整个videoData
|
||||||
|
Object.assign(videoData, data)
|
||||||
|
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('资源加载失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '资源加载失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new Error(`请求失败: ${res.statusCode}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取视频数据失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载视频失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('获取视频列表失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '网络连接异常',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.videopage {
|
main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
max-width: 430px;
|
||||||
|
max-height: 980px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacerview {
|
||||||
|
flex: 1;
|
||||||
|
pointer-events: none
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 视频容器 */
|
||||||
|
.video-container {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 430px;
|
max-width: 430px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
max-height: 980px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 680px;
|
||||||
|
object-fit: cover;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
position: relative;
|
z-index: 0;
|
||||||
}
|
flex: 1;
|
||||||
|
|
||||||
.videopage video {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 430px;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 680px;
|
|
||||||
max-height: 980px;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .uni-video-cover {
|
::v-deep .uni-video-cover {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep .uni-video-cover-play-button {
|
::v-deep .uni-video-cover-play-button {
|
||||||
@@ -258,12 +352,13 @@ const toggleMute = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 弹幕区域 */
|
/* 弹幕区域 */
|
||||||
::v-deep .uni-video-danmu {
|
::v-deep .uni-video-danmu {
|
||||||
height: 15% !important;
|
height: 15% !important;
|
||||||
z-index: 2;
|
|
||||||
margin: 32px 0 0;
|
margin: 32px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,6 +372,7 @@ const toggleMute = () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 用户信息区域 */
|
/* 用户信息区域 */
|
||||||
@@ -321,10 +417,7 @@ const toggleMute = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
text-align: center;
|
||||||
padding: 8px;
|
|
||||||
border-radius: 8px;
|
|
||||||
transition: background-color 0.3s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-icon {
|
.action-icon {
|
||||||
@@ -333,6 +426,11 @@ const toggleMute = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.action-count {
|
.action-count {
|
||||||
|
width: 100%;
|
||||||
|
height: 17px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -352,11 +450,12 @@ const toggleMute = () => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 2;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vedioinfohead {
|
.vedioinfohead {
|
||||||
@@ -365,34 +464,25 @@ const toggleMute = () => {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.username,
|
||||||
|
.datetime {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.username {
|
.username {
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-stretch: normal;
|
/* font-family: 'SFPro'; */
|
||||||
font-style: normal;
|
|
||||||
line-height: normal;
|
|
||||||
letter-spacing: normal;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
display: flex;
|
line-height: 1;
|
||||||
align-items: center;
|
|
||||||
height: 24px;
|
|
||||||
white-space: nowrap;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.datetime {
|
.datetime {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-stretch: normal;
|
|
||||||
font-style: normal;
|
|
||||||
line-height: normal;
|
|
||||||
letter-spacing: normal;
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 24px;
|
|
||||||
white-space: nowrap;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@@ -460,7 +550,6 @@ const toggleMute = () => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-video-icon {
|
.uni-video-icon {
|
||||||
@@ -480,40 +569,6 @@ const toggleMute = () => {
|
|||||||
cursor: pointer;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uni-video-icon {
|
|
||||||
font-family: 'uni-video-icon';
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uni-video-cover-play-button {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
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 {
|
.uni-video-cover-play-button::after {
|
||||||
content: '\ea24';
|
content: '\ea24';
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 15 KiB |
BIN
src/static/imgs/errorload/errorload.webp
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src/static/imgs/errorload/errorload@2x.webp
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
src/static/imgs/errorload/errorload@3x.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 37 KiB |
BIN
src/static/imgs/nocomments/nocomments.webp
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/static/imgs/nocomments/nocomments@2x.webp
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
src/static/imgs/nocomments/nocomments@3x.webp
Normal file
|
After Width: | Height: | Size: 10 KiB |
@@ -26,6 +26,64 @@ export const useCommonStore = defineStore('common', () => {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将评论内容转换为富文本节点
|
||||||
|
* @param {string} content - 评论内容
|
||||||
|
* @param {string[]} atUsers - @用户数组
|
||||||
|
* @returns {Array} rich-text nodes
|
||||||
|
*/
|
||||||
|
function contentToRenderNodes(content = '', atUsers = []) {
|
||||||
|
if (!content) {
|
||||||
|
return [{ type: 'text', text: '' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有@用户,处理@高亮
|
||||||
|
if (atUsers && atUsers.length > 0) {
|
||||||
|
const nodes = []
|
||||||
|
let remainingContent = content
|
||||||
|
|
||||||
|
// 为每个@用户创建高亮节点
|
||||||
|
atUsers.forEach(user => {
|
||||||
|
const atText = `@${user}`
|
||||||
|
const atIndex = remainingContent.indexOf(atText)
|
||||||
|
|
||||||
|
if (atIndex !== -1) {
|
||||||
|
// 添加@前面的普通文本
|
||||||
|
if (atIndex > 0) {
|
||||||
|
nodes.push({
|
||||||
|
type: 'text',
|
||||||
|
text: remainingContent.substring(0, atIndex)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加@高亮节点
|
||||||
|
nodes.push({
|
||||||
|
type: 'node',
|
||||||
|
name: 'span',
|
||||||
|
attrs: { style: 'color:#0969DA;', 'data-name': user },
|
||||||
|
children: [{ type: 'text', text: atText }]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新剩余内容
|
||||||
|
remainingContent = remainingContent.substring(atIndex + atText.length)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加剩余文本
|
||||||
|
if (remainingContent) {
|
||||||
|
nodes.push({
|
||||||
|
type: 'text',
|
||||||
|
text: remainingContent
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有@用户,直接返回普通文本
|
||||||
|
return [{ type: 'text', text: content }]
|
||||||
|
}
|
||||||
|
|
||||||
/* 日期处理函数 */
|
/* 日期处理函数 */
|
||||||
/**
|
/**
|
||||||
* 格式化日期,如果年份是当年则不显示年份
|
* 格式化日期,如果年份是当年则不显示年份
|
||||||
@@ -39,12 +97,12 @@ export const useCommonStore = defineStore('common', () => {
|
|||||||
|
|
||||||
if (inputYear === currentYear) {
|
if (inputYear === currentYear) {
|
||||||
// 当年:显示月份和日期
|
// 当年:显示月份和日期
|
||||||
return `${inputDate.getMonth() + 1}-${inputDate.getDate()}`;
|
return `${inputDate.getMonth() + 1}/${inputDate.getDate()}`;
|
||||||
} else {
|
} else {
|
||||||
// 非当年:显示完整年月日
|
// 非当年:显示完整年月日
|
||||||
return `${inputYear}-${inputDate.getMonth() + 1}-${inputDate.getDate()}`;
|
return `${inputYear}/${inputDate.getMonth() + 1}/${inputDate.getDate()}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { download, openapp, formatCount, atUsersToNodes, formatDate }
|
return { download, openapp, formatCount, atUsersToNodes, formatDate, contentToRenderNodes }
|
||||||
})
|
})
|
||||||
@@ -5,4 +5,13 @@ export default defineConfig({
|
|||||||
plugins: [
|
plugins: [
|
||||||
uni(),
|
uni(),
|
||||||
],
|
],
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://192.168.1.7:8088', // 请替换为你的实际后端服务器地址
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, '/api')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||