YingXingAI/components/ContentCard.vue

723 lines
18 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 class="content-card">
<!-- 用户信息 -->
<view class="user-info">
<image
class="avatar"
:src="cardData.user.avatar"
mode="aspectFill"
></image>
<view class="user-detail">
<text class="username">{{ cardData.user.name }}</text>
<text class="user-meta">{{ cardData.user.meta }}</text>
</view>
<view class="follow-btn" @click="onFollow">
<u-icon name="plus" color="#666" size="14"></u-icon>
<text>关注</text>
</view>
</view>
<!-- 内容文本 -->
<view class="content-text" @click="goContentDetail">
{{ cardData.content }}
</view>
<!-- 媒体内容 - 内部处理交互 -->
<view class="media-content">
<!-- 视频内容 -->
<view v-if="isVideo" class="video-container" @click="handleVideoPlay">
<image
class="video-cover"
:src="cardData.cover"
mode="aspectFill"
></image>
<view class="video-overlay">
<u-icon name="play-right" color="#fff" size="40"></u-icon>
<text v-if="cardData.duration" class="video-duration">{{
formatDuration(cardData.duration)
}}</text>
</view>
</view>
<!-- 图片内容 -->
<view v-else class="image-grid" :class="getImageLayoutClass()">
<image
v-for="(img, index) in cardData.images"
:key="index"
class="grid-image"
:src="img"
mode="aspectFill"
@click="handleImagePreview(index)"
></image>
</view>
</view>
<!-- 互动栏 - 内部处理交互 -->
<view class="interaction-bar">
<view
class="action-item"
v-for="(item, index) in actionButtons"
:key="index"
@click="handleAction(item.action)"
>
<image
:src="item.icon"
mode="aspectFill"
style="width: 40rpx; height: 40rpx"
></image>
<text>{{ cardData.stats[item.stat] }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
name: "ContentCard",
props: {
cardData: {
type: Object,
required: true,
default: () => ({
user: {
avatar: "",
name: "",
meta: "",
},
content: "",
// 可以是视频或图片
type: "image", // 'image' 或 'video'
images: [], // 图片数组
cover: "", // 视频封面
videoUrl: "", // 视频地址
duration: 0, // 视频时长(秒)
stats: {
forward: 0,
comment: 0,
like: 0,
favorite: 0,
},
}),
},
},
data() {
return {
// 添加内部状态跟踪互动状态
isLiked: false,
isFavorited: false,
isForwarded: false,
videoContext: null,
// 添加操作按钮配置
actionButtons: [
{
icon: "/static/common/img/homepage/reply.png",
action: "forward",
stat: "forward"
},
{
icon: "/static/common/img/homepage/comment.png",
action: "comment",
stat: "comment"
},
{
icon: "/static/common/img/homepage/like.png",
action: "like",
stat: "like"
},
{
icon: "/static/common/img/homepage/star.png",
action: "favorite",
stat: "favorite"
}
]
};
},
computed: {
isVideo() {
return this.cardData.type === "video";
},
// 根据状态动态改变图标和颜色
likeIcon() {
return this.isLiked ? "heart-fill" : "heart";
},
likeColor() {
return this.isLiked ? "#ff5252" : "#999";
},
favoriteIcon() {
return this.isFavorited ? "star-fill" : "star";
},
favoriteColor() {
return this.isFavorited ? "#ffb700" : "#999";
},
forwardIcon() {
return this.isForwarded ? "arrow-right-fill" : "arrow-right";
},
forwardColor() {
return this.isForwarded ? "#2979ff" : "#999";
},
commentColor() {
return "#999"; // 评论按钮颜色无变化
},
},
methods: {
// 处理关注点击
onFollow() {
this.$emit("follow", this.cardData.user);
},
// 跳转详情页
goContentDetail() {
console.log("this.cardData", this.cardData);
this.$u.route({
url: "/pages/home/home/components/contentDetail/index",
// params: {
// card,
// },
});
},
// 格式化视频时长
formatDuration(seconds) {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
return `${minutes.toString().padStart(2, "0")}:${remainingSeconds
.toString()
.padStart(2, "0")}`;
},
// 处理视频播放
handleVideoPlay() {
if (this.cardData.videoUrl) {
// 记录播放历史
this.savePlayHistory();
// 使用内置视频播放器播放
try {
// 先尝试使用内部视频播放器
if (this.useInternalPlayer) {
this.showVideoPlayer = true;
this.$nextTick(() => {
this.videoContext = uni.createVideoContext("video-player", this);
this.videoContext.requestFullScreen();
this.videoContext.play();
});
} else {
// 否则跳转到专门的播放页面
uni.navigateTo({
url: `/pages/video/player?videoUrl=${encodeURIComponent(
this.cardData.videoUrl
)}&title=${encodeURIComponent(this.cardData.content)}`,
});
}
// 触发播放事件,让父组件可以进行额外处理
this.$emit("video-play", {
videoUrl: this.cardData.videoUrl,
title: this.cardData.content,
cover: this.cardData.cover,
cardId: this.cardData.id,
});
// 更新播放统计
this.updateVideoStats();
} catch (error) {
console.error("视频播放失败:", error);
uni.showToast({
title: "视频播放失败,请稍后再试",
icon: "none",
});
}
}
},
// 处理图片预览
handleImagePreview(index) {
try {
// 使用uni-app的图片预览API
uni.previewImage({
current: index,
urls: this.cardData.images,
longPressActions: {
itemList: ["保存图片", "分享图片"],
success: (res) => {
const { tapIndex } = res;
if (tapIndex === 0) {
// 保存图片
this.saveImage(this.cardData.images[index]);
} else if (tapIndex === 1) {
// 分享图片
this.shareImage(this.cardData.images[index]);
}
},
},
});
// 触发图片预览事件
this.$emit("image-preview", {
index,
images: this.cardData.images,
cardId: this.cardData.id,
});
} catch (error) {
console.error("图片预览失败:", error);
}
},
// 保存图片
saveImage(url) {
uni.showLoading({ title: "正在保存..." });
uni.downloadFile({
url,
success: (res) => {
if (res.statusCode === 200) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.showToast({ title: "保存成功" });
},
fail: (err) => {
console.error("保存失败:", err);
uni.showToast({ title: "保存失败", icon: "none" });
},
});
}
},
fail: () => {
uni.showToast({ title: "下载图片失败", icon: "none" });
},
complete: () => {
uni.hideLoading();
},
});
},
// 分享图片
shareImage(url) {
// 根据平台实现不同的分享逻辑
// #ifdef APP-PLUS
plus.share.sendWithSystem({
type: "image",
pictures: [url],
});
// #endif
// #ifdef H5
// H5环境可能需要调用其他API
// #endif
// #ifdef MP
// 小程序环境使用小程序分享API
uni.showToast({ title: "请使用右上角分享", icon: "none" });
// #endif
},
// 根据图片数量确定布局样式
getImageLayoutClass() {
const count = this.cardData.images.length;
if (count === 1) return "single-image";
if (count === 2) return "double-image";
if (count === 3) return "triple-image";
if (count === 4) return "four-image";
if (count > 4) return "multi-image";
return "";
},
// 处理转发
handleForward() {
this.isForwarded = !this.isForwarded;
// 显示分享菜单
uni.showShareMenu({
withShareTicket: true,
menus: ["shareAppMessage", "shareTimeline"],
});
// 触发转发事件
this.$emit("forward", {
cardData: this.cardData,
isForwarded: this.isForwarded,
});
// 如果在APP中可以调用原生分享
// #ifdef APP-PLUS
uni.share({
provider: "weixin",
scene: "WXSceneSession",
type: 0,
title: this.cardData.content,
summary: this.cardData.content,
imageUrl: this.isVideo ? this.cardData.cover : this.cardData.images[0],
success: function (res) {
console.log("分享成功:" + JSON.stringify(res));
},
fail: function (err) {
console.log("分享失败:" + JSON.stringify(err));
},
});
// #endif
},
// 处理评论
handleComment() {
// 跳转到评论页面或打开评论弹窗
this.$emit("comment", {
cardData: this.cardData,
});
// 这里可以实现自定义的评论交互
uni.navigateTo({
url: `/pages/comment/index?id=${this.cardData.id}&type=${this.cardData.type}`,
});
},
// 处理点赞
handleLike() {
// 切换点赞状态
this.isLiked = !this.isLiked;
// 更新点赞数据
const newStats = { ...this.cardData.stats };
if (this.isLiked) {
newStats.like += 1;
} else {
newStats.like = Math.max(0, newStats.like - 1);
}
// 更新数据并触发事件
this.$emit("like", {
cardData: this.cardData,
isLiked: this.isLiked,
newStats,
});
// 发送点赞请求到服务器
this.updateLikeStatus();
},
// 处理收藏
handleFavorite() {
// 切换收藏状态
this.isFavorited = !this.isFavorited;
// 更新收藏数据
const newStats = { ...this.cardData.stats };
if (this.isFavorited) {
newStats.favorite += 1;
} else {
newStats.favorite = Math.max(0, newStats.favorite - 1);
}
// 更新数据并触发事件
this.$emit("favorite", {
cardData: this.cardData,
isFavorited: this.isFavorited,
newStats,
});
// 发送收藏请求到服务器
this.updateFavoriteStatus();
},
// 更新点赞状态到服务器
updateLikeStatus() {
// 调用API更新点赞状态
// 示例代码根据实际API调整
const url = this.isLiked ? "/api/like/add" : "/api/like/cancel";
uni.request({
url,
method: "POST",
data: {
contentId: this.cardData.id,
contentType: this.cardData.type,
},
success: (res) => {
console.log("点赞状态更新成功", res);
},
fail: (err) => {
console.error("点赞状态更新失败", err);
// 恢复原状态
this.isLiked = !this.isLiked;
},
});
},
// 更新收藏状态到服务器
updateFavoriteStatus() {
// 调用API更新收藏状态
// 示例代码根据实际API调整
const url = this.isFavorited
? "/api/favorite/add"
: "/api/favorite/cancel";
uni.request({
url,
method: "POST",
data: {
contentId: this.cardData.id,
contentType: this.cardData.type,
},
success: (res) => {
console.log("收藏状态更新成功", res);
},
fail: (err) => {
console.error("收藏状态更新失败", err);
// 恢复原状态
this.isFavorited = !this.isFavorited;
},
});
},
// 记录视频播放历史
savePlayHistory() {
// 保存播放历史到本地存储或服务器
try {
const historyList = uni.getStorageSync("videoPlayHistory") || [];
const now = new Date().getTime();
// 查找是否已有该视频记录
const index = historyList.findIndex(
(item) => item.id === this.cardData.id
);
if (index > -1) {
// 更新已有记录
historyList[index].lastPlayTime = now;
historyList[index].playCount += 1;
} else {
// 添加新记录
historyList.push({
id: this.cardData.id,
title: this.cardData.content,
cover: this.cardData.cover,
lastPlayTime: now,
playCount: 1,
});
}
// 保存回本地存储
uni.setStorageSync("videoPlayHistory", historyList);
} catch (e) {
console.error("保存播放历史失败", e);
}
},
// 更新视频播放统计
updateVideoStats() {
// 调用API更新视频播放次数
uni.request({
url: "/api/video/view",
method: "POST",
data: {
videoId: this.cardData.id,
},
success: (res) => {
console.log("视频播放统计更新成功", res);
},
fail: (err) => {
console.error("视频播放统计更新失败", err);
},
});
},
// 添加统一的操作处理方法
handleAction(action) {
switch(action) {
case "forward":
this.handleForward();
break;
case "comment":
this.handleComment();
break;
case "like":
this.handleLike();
break;
case "favorite":
this.handleFavorite();
break;
}
}
},
};
</script>
<style scoped lang="scss">
.content-card {
background-color: #fff;
border-radius: 24rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
.user-info {
display: flex;
align-items: center;
margin-bottom: 24rpx;
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.user-detail {
flex: 1;
.username {
font-size: 30rpx;
font-weight: 500;
color: #333;
display: block;
line-height: 1.2;
}
.user-meta {
font-size: 24rpx;
color: #999;
display: block;
line-height: 1.2;
margin-top: 6rpx;
}
}
.follow-btn {
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
border-radius: 30rpx;
padding: 8rpx 16rpx;
text {
font-size: 24rpx;
color: #666;
margin-left: 4rpx;
}
}
}
.content-text {
font-size: 28rpx;
color: #333;
line-height: 1.5;
margin-bottom: 24rpx;
}
.media-content {
margin-bottom: 24rpx;
width: 100%;
// 视频容器样式
.video-container {
position: relative;
width: 100%;
border-radius: 8rpx;
overflow: hidden;
.video-cover {
width: 100%;
height: 400rpx;
display: block;
}
.video-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.2);
.video-duration {
position: absolute;
right: 16rpx;
bottom: 16rpx;
color: #fff;
font-size: 24rpx;
background-color: rgba(0, 0, 0, 0.5);
padding: 4rpx 8rpx;
border-radius: 4rpx;
}
}
}
// 图片网格样式
.image-grid {
display: flex;
flex-wrap: wrap;
.grid-image {
border-radius: 8rpx;
background-color: #f5f5f5;
}
&.single-image {
.grid-image {
width: 100%;
max-height: 400rpx;
}
}
&.double-image {
justify-content: space-between;
.grid-image {
width: 49%;
height: 240rpx;
}
}
&.triple-image {
justify-content: space-between;
.grid-image {
width: 31%;
height: 180rpx;
}
}
&.four-image {
justify-content: space-between;
.grid-image {
width: 49%;
height: 180rpx;
margin-bottom: 10rpx;
}
}
&.multi-image {
justify-content: space-between;
.grid-image {
width: 31%;
height: 180rpx;
margin-bottom: 10rpx;
}
}
}
}
.interaction-bar {
display: flex;
justify-content: space-between;
margin-top: 16rpx;
padding: 0 20rpx;
.action-item {
display: flex;
align-items: center;
text {
font-size: 24rpx;
color: #999;
margin-left: 8rpx;
}
}
}
}
</style>