Files
msh-system/msh_single_uniapp/pages/ai-generate/video.vue

1374 lines
32 KiB
Vue
Raw Normal View History

<template>
<view class="video-container" @touchstart="onTouchStart" @touchend="onTouchEnd">
<video
id="video-element"
class="video-player"
:src="videoUrl"
:autoplay="autoplay"
:muted="muted"
:controls="false"
loop
object-fit="cover"
@play="onVideoPlay"
@pause="onVideoPause"
@ended="onVideoEnd"
@timeupdate="onTimeUpdate"
@loadedmetadata="onVideoReady"
@click="onVideoTap"
></video>
<!-- 顶部控制栏 -->
<view class="top-controls" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="left-controls">
<view class="back-btn" @click="onBack">
<text class="icon-back"></text>
</view>
<text class="time-display">{{ currentTime }}</text>
</view>
</view>
<!-- 视频内容区域 -->
<view class="video-content" @click="onPlayPause">
<view :class="['play-indicator', isPlaying ? 'hide' : 'show']">
<text class="play-icon">&#9654;</text>
</view>
</view>
<!-- 底部信息区域 -->
<view class="bottom-info">
<!-- 文章加载状态 -->
<view class="article-status" v-if="isLoadingArticle || articleError">
<view class="loading-indicator" v-if="isLoadingArticle">
<text class="loading-text">正在加载文章详情...</text>
</view>
<view class="error-message" v-if="articleError">
<text class="error-text">{{ articleError }}</text>
</view>
</view>
<!-- 创作者信息 -->
<view class="creator-info">
<image class="creator-avatar" :src="creatorAvatar" @click="onCreatorTap" />
<view class="creator-details">
<text class="creator-name">{{ creatorName }}</text>
<view :class="['follow-btn', isFollowing ? 'following' : '']" @click="onFollow">
{{ isFollowing ? '已关注' : '关注' }}
</view>
</view>
</view>
<!-- 视频描述 -->
<view class="video-info" v-if="videoDescription">
<view :class="['video-description', isDescriptionExpanded ? 'expanded' : '']" @click="onToggleDescription">
{{ videoDescription }}
</view>
<view class="expand-toggle" @click="onToggleDescription" v-if="videoDescription.length > 50">
<text class="iconfont" :class="isDescriptionExpanded ? 'icon-xiangshang' : 'icon-xiangxia'"></text>
</view>
</view>
<!-- 互动区域 -->
<view class="interaction-area">
<view class="like-section">
<view :class="['like-btn', isLiked ? 'liked' : '']" @click="onLike">
<text class="heart-icon">{{ isLiked ? '❤️' : '🤍' }}</text>
</view>
<text class="like-count">{{ likeCount }}</text>
</view>
<view class="action-buttons">
<view class="consult-btn" @click="onConsult">立即咨询</view>
<view class="action-btn" @click="onCreateSimilar">做同款</view>
</view>
</view>
<!-- AI生成内容标识 -->
<view class="ai-notice">
<text class="ai-notice-text">内容由AI生成</text>
</view>
<!-- 进度条 -->
<view class="progress-bar">
<view class="progress-track">
<view class="progress-fill" :style="{ width: progress + '%' }"></view>
</view>
</view>
</view>
<!-- 悬浮操作按钮 -->
<view class="floating-actions">
<view class="floating-btn download-btn" @click="onDownload">
<text class="floating-label">下载</text>
</view>
<view class="floating-btn share-btn" @click="onShare">
<text class="floating-label">分享</text>
</view>
</view>
</view>
</template>
<script>
import api from '@/api/models-api.js';
import { mapGetters } from 'vuex';
export default {
data() {
return {
videoUrl: 'https://jxz.uj345.cc/videos/video1.mp4',
currentTime: '00:25',
progress: 45,
isPlaying: false,
autoplay: false,
muted: false,
creatorName: '开心海浪',
creatorAvatar: '/static/images/creator-avatar.png',
isFollowing: false,
videoDescription: '女孩自然的姿势贴地快速飞行,飞向湖面,镜头跟随其后,女孩自然的姿势贴地快速飞行,飞向湖面,镜头跟随其后,女孩自然的姿势贴地快速飞行,飞向湖面,镜头跟随其后',
isDescriptionExpanded: false,
isLiked: false,
likeCount: 2778,
videoContext: null,
isVideoReady: false,
playPromise: null,
articleData: null,
isLoadingArticle: false,
articleError: null,
currentArticleId: null,
touchStartY: 0,
touchEndY: 0,
isSwipeEnabled: true,
statusBarHeight: 0,
};
},
computed: mapGetters(['chatUrl']),
onLoad(options) {
const articleId = options.id;
this.setData({
currentArticleId: articleId,
});
const sys = uni.getSystemInfoSync();
const sbh = sys.statusBarHeight;
this.statusBarHeight = (sbh && sbh > 0) ? sbh : (sys.platform === 'ios' ? 44 : 24);
if (articleId) {
console.log('开始加载文章详情ID:', articleId);
this.loadArticleDetail(articleId);
} else {
console.log('未提供文章ID跳过文章详情加载');
}
setTimeout(() => {
this.videoContext = uni.createVideoContext('video-element', this);
this.setData({
isVideoReady: true,
});
}, 1000);
},
onShow() {
setTimeout(() => {
if (this.videoContext && this.isVideoReady) {
this.safePlay();
}
}, 300);
},
onHide() {
if (this.videoContext && this.isVideoReady) {
this.safePause();
}
},
methods: {
sanitizeText(str) {
if (!str || typeof str !== 'string') return '';
try {
// 去除所有 HTML 标签,避免 WXML 解析原始 `<` `>` 造成语法错误
const noTags = str.replace(/<[^>]*>/g, '');
// 保险处理:将残留的尖括号转义为实体
return noTags.replace(/</g, '&lt;').replace(/>/g, '&gt;');
} catch (e) {
return String(str);
}
},
// 处理头像URL添加前缀
getAvatarUrl(avatarUrl) {
if (!avatarUrl || avatarUrl === '/static/images/avatar-default.png') {
return '/static/images/avatar-default.png';
}
// 如果已经是完整URL直接返回
if (avatarUrl.startsWith('http://') || avatarUrl.startsWith('https://')) {
return avatarUrl;
}
// 如果是相对路径,添加前缀
const prefix = 'https://uthink2025.oss-cn-shanghai.aliyuncs.com/';
// 如果URL已经以/开头,去掉开头的/
const cleanUrl = avatarUrl.startsWith('/') ? avatarUrl.substring(1) : avatarUrl;
return prefix + cleanUrl;
},
loadVideoData() {
setTimeout(() => {
this.setData({
videoUrl: '/videos/sample-video.mp4',
});
}, 500);
},
loadArticleDetail(articleId) {
console.log('loadArticleDetail 被调用,参数:', articleId);
this.setData({
isLoadingArticle: true,
articleError: null,
});
console.log('设置加载状态为 true');
api.getArticleById(articleId).then(response => {
console.log('API响应原始数据:', response);
const data = response.data || response;
console.log('文章详情数据:', data);
const rawDesc = data.prompt || data.content || data.title || data.synopsis || this.videoDescription;
const safeDesc = this.sanitizeText(rawDesc);
// 获取作者头像
const authorAvatar = this.getAvatarUrl(data.authorAvatar || data.avatar || '');
// 获取作者名称
const authorName = data.authorName || data.author || this.creatorName;
// 获取点赞数
const likeCount = data.likeCount || data.visit || this.likeCount;
this.setData({
articleData: data,
isLoadingArticle: false,
videoDescription: safeDesc,
creatorName: authorName,
creatorAvatar: authorAvatar,
videoUrl: data.videoUrl || this.videoUrl,
likeCount: likeCount,
});
console.log('页面数据更新完成,当前 articleData:', this.articleData);
console.log('当前 isLoadingArticle:', this.isLoadingArticle);
console.log('作者头像:', authorAvatar);
}).catch(error => {
console.error('文章详情获取失败:', error);
this.setData({
articleError: error.message || '获取文章详情失败',
isLoadingArticle: false,
});
console.log('错误状态设置完成articleError:', this.articleError);
uni.showToast({
title: '获取文章详情失败',
icon: 'none',
duration: 2000,
});
});
},
onBack() {
uni.navigateBack();
},
// 下载视频
onDownload() {
if (!this.videoUrl) {
uni.showToast({
title: '视频地址不存在',
icon: 'none'
});
return;
}
uni.showLoading({
title: '下载中...',
mask: true
});
// 下载视频文件
uni.downloadFile({
url: this.videoUrl,
success: (res) => {
if (res.statusCode === 200) {
// 保存到相册
uni.saveVideoToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.hideLoading();
uni.showToast({
title: '已保存到相册',
icon: 'success',
duration: 2000
});
},
fail: (error) => {
uni.hideLoading();
console.error('保存到相册失败:', error);
// 如果是权限问题,提示用户开启权限
if (error.errMsg.includes('auth')) {
uni.showModal({
title: '需要相册权限',
content: '请在设置中开启相册权限',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting();
}
}
});
} else {
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
}
});
} else {
uni.hideLoading();
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
},
fail: (error) => {
uni.hideLoading();
console.error('下载失败:', error);
uni.showToast({
title: '下载失败,请重试',
icon: 'none'
});
}
});
},
// 分享视频
onShare() {
uni.showActionSheet({
itemList: ['分享到微信', '分享到朋友圈', '复制链接'],
success: (res) => {
if (res.tapIndex === 0) {
// 分享到微信好友
this.shareToWechat();
} else if (res.tapIndex === 1) {
// 分享到朋友圈
this.shareToMoments();
} else if (res.tapIndex === 2) {
// 复制链接
this.copyLink();
}
}
});
},
// 分享到微信好友
shareToWechat() {
// #ifdef MP-WEIXIN
// 小程序中使用按钮的 open-type="share"
uni.showToast({
title: '请使用右上角分享',
icon: 'none'
});
// #endif
// #ifdef H5
uni.showToast({
title: '请使用浏览器分享功能',
icon: 'none'
});
// #endif
// #ifdef APP-PLUS
// App中可以调用原生分享
uni.share({
provider: 'weixin',
scene: 'WXSceneSession',
type: 2,
title: this.videoDescription || '精彩视频分享',
summary: `来自 ${this.creatorName} 的视频`,
href: this.videoUrl,
imageUrl: this.articleData?.imageInput || '',
success: () => {
uni.showToast({
title: '分享成功',
icon: 'success'
});
},
fail: (err) => {
console.error('分享失败:', err);
uni.showToast({
title: '分享失败',
icon: 'none'
});
}
});
// #endif
},
// 分享到朋友圈
shareToMoments() {
// #ifdef MP-WEIXIN
uni.showToast({
title: '请使用右上角分享到朋友圈',
icon: 'none'
});
// #endif
// #ifdef H5
uni.showToast({
title: '请使用浏览器分享功能',
icon: 'none'
});
// #endif
// #ifdef APP-PLUS
uni.share({
provider: 'weixin',
scene: 'WXSceneTimeline',
type: 2,
title: this.videoDescription || '精彩视频分享',
summary: `来自 ${this.creatorName} 的视频`,
href: this.videoUrl,
imageUrl: this.articleData?.imageInput || '',
success: () => {
uni.showToast({
title: '分享成功',
icon: 'success'
});
},
fail: (err) => {
console.error('分享失败:', err);
uni.showToast({
title: '分享失败',
icon: 'none'
});
}
});
// #endif
},
// 复制链接
copyLink() {
const shareUrl = this.currentArticleId
? `https://yourapp.com/pages/ai-generate/video?id=${this.currentArticleId}`
: 'https://yourapp.com/pages/ai-generate/video';
uni.setClipboardData({
data: shareUrl,
success: () => {
uni.showToast({
title: '链接已复制',
icon: 'success'
});
},
fail: () => {
uni.showToast({
title: '复制失败',
icon: 'none'
});
}
});
},
onVideoTap(e) {
if (e && e.stopPropagation) {
e.stopPropagation();
}
this.onPlayPause();
},
onVideoLongPress(e) {
if (e && e.stopPropagation) {
e.stopPropagation();
}
uni.showActionSheet({
itemList: ['举报', '不感兴趣', '保存到相册'],
success: res => {
const actions = ['举报', '不感兴趣', '保存到相册'];
uni.showToast({
title: actions[res.tapIndex],
icon: 'none',
});
},
});
},
onPlayPause(e) {
if (e && e.stopPropagation) {
e.stopPropagation();
}
if (!this.videoContext || !this.isVideoReady) {
console.warn('视频上下文未准备就绪');
return;
}
if (this.isPlaying) {
this.safePause();
} else {
this.safePlay();
}
},
safePlay() {
if (!this.videoContext || !this.isVideoReady) {
return;
}
if (this.isPlaying) {
return;
}
try {
this.videoContext.play();
} catch (error) {
console.error('播放失败:', error);
}
},
safePause() {
if (!this.videoContext || !this.isVideoReady) {
return;
}
if (!this.isPlaying) {
return;
}
try {
this.videoContext.pause();
} catch (error) {
console.error('暂停失败:', error);
}
},
onVideoReady() {
console.log('视频元数据加载完成');
this.setData({
isVideoReady: true,
});
},
onVideoPlay() {
console.log('视频开始播放');
this.setData({
isPlaying: true,
});
},
onVideoPause() {
console.log('视频暂停');
this.setData({
isPlaying: false,
});
},
onTimeUpdate(e) {
const { currentTime, duration } = e.detail;
const progress = (currentTime / duration) * 100;
const minutes = Math.floor(currentTime / 60);
const seconds = Math.floor(currentTime % 60);
const timeStr = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
this.setData({
currentTime: timeStr,
progress: progress,
});
},
onVideoEnd() {
this.setData({
isPlaying: false,
progress: 0,
});
},
onCreatorTap() {
uni.showToast({
title: `查看${this.creatorName}的主页`,
icon: 'none',
});
},
onFollow() {
const isFollowing = !this.isFollowing;
this.setData({
isFollowing: isFollowing,
});
uni.showToast({
title: isFollowing ? '关注成功' : '取消关注',
icon: 'success',
});
},
onLike() {
const isLiked = !this.isLiked;
const likeCount = isLiked ? this.likeCount + 1 : this.likeCount - 1;
this.setData({
isLiked: isLiked,
likeCount: likeCount,
});
if (isLiked) {
uni.vibrateShort();
}
},
onCreateSimilar() {
const description = this.videoDescription;
const encodedDescription = encodeURIComponent(description);
uni.navigateTo({
url: `/pages/ai-generate/oneclick?description=${encodedDescription}&from=video`,
});
},
onConsult() {
const url = this.chatUrl;
if (!url) {
uni.showToast({ title: '暂无客服链接', icon: 'none' });
return;
}
// 统一跳转到内置网页容器
uni.navigateTo({
url: `/pages/users/web_page/index?webUel=${encodeURIComponent(url)}&title=客服`
});
},
onViewHistory() {
uni.navigateTo({
url: '/pages/ai-generate/assets',
});
},
onTouchStart(e) {
if (!this.isSwipeEnabled || this.isLoadingArticle) return;
this.setData({
touchStartY: e.touches[0].clientY,
});
},
onTouchEnd(e) {
if (!this.isSwipeEnabled || this.isLoadingArticle) return;
const touchEndY = e.changedTouches[0].clientY;
const deltaY = touchEndY - this.touchStartY;
const minSwipeDistance = 50;
this.setData({
touchEndY: touchEndY,
});
if (Math.abs(deltaY) > minSwipeDistance) {
if (deltaY > 0) {
this.loadPreviousArticle();
} else {
this.loadNextArticle();
}
}
},
loadPreviousArticle() {
const currentId = parseInt(this.currentArticleId);
if (currentId && currentId > 1) {
const previousId = currentId - 1;
console.log('加载上一篇文章ID:', previousId);
this.loadArticleDetail(previousId);
this.setData({
currentArticleId: previousId.toString(),
});
} else {
uni.showToast({
title: '已经是第一篇了',
icon: 'none',
});
}
},
loadNextArticle() {
const currentId = parseInt(this.currentArticleId);
if (currentId) {
const nextId = currentId + 1;
console.log('加载下一篇文章ID:', nextId);
this.loadArticleDetail(nextId);
this.setData({
currentArticleId: nextId.toString(),
});
}
},
onToggleDescription() {
this.setData({
isDescriptionExpanded: !this.isDescriptionExpanded,
});
},
onShareAppMessage() {
try {
// 获取分享标题优先使用articleData.title
let shareTitle = '精彩视频分享'; // 默认标题
if (this.articleData && this.articleData.title) {
shareTitle = this.articleData.title;
// 限制标题长度微信建议不超过28个字符
if (shareTitle.length > 28) {
shareTitle = shareTitle.substring(0, 25) + '...';
}
} else if (this.creatorName) {
shareTitle = `${this.creatorName}的作品`;
}
// 获取分享缩略图优先使用articleData.imageInput
let shareImageUrl = '/static/images/video-share.png'; // 默认缩略图
if (this.articleData && this.articleData.imageInput) {
shareImageUrl = this.articleData.imageInput;
}
// 构建分享路径包含当前文章ID
const sharePath = this.currentArticleId
? `/pages/ai-generate/video?id=${this.currentArticleId}`
: '/pages/ai-generate/assets';
console.log('分享配置:', {
title: shareTitle,
path: sharePath,
imageUrl: shareImageUrl
});
return {
title: shareTitle,
path: sharePath,
imageUrl: shareImageUrl,
};
} catch (error) {
console.error('分享配置失败:', error);
// 发生错误时返回默认配置
return {
title: '精彩视频分享',
path: '/pages/ai-generate/video',
imageUrl: '/static/images/video-share.png',
};
}
},
onShareTimeline() {
try {
// 获取分享标题优先使用articleData.title
let shareTitle = '精彩视频分享'; // 默认标题
if (this.articleData && this.articleData.title) {
shareTitle = this.articleData.title;
// 朋友圈分享标题建议更简洁限制在20个字符内
if (shareTitle.length > 20) {
shareTitle = shareTitle.substring(0, 17) + '...';
}
} else if (this.creatorName) {
shareTitle = `${this.creatorName}的作品`;
}
// 获取分享缩略图优先使用articleData.imageInput
let shareImageUrl = '/static/images/video-share.png'; // 默认缩略图
if (this.articleData && this.articleData.imageInput) {
shareImageUrl = this.articleData.imageInput;
}
console.log('朋友圈分享配置:', {
title: shareTitle,
imageUrl: shareImageUrl
});
return {
title: shareTitle,
imageUrl: shareImageUrl,
};
} catch (error) {
console.error('朋友圈分享配置失败:', error);
// 发生错误时返回默认配置
return {
title: '精彩视频分享',
imageUrl: '/static/images/video-share.png',
};
}
},
onPullDownRefresh() {
this.loadVideoData();
setTimeout(() => {
uni.stopPullDownRefresh();
}, 1000);
},
setData(data) {
let that = this;
Object.keys(data).forEach(key => {
that[key] = data[key];
});
},
},
};
</script>
<style>
/* 视频播放页面样式 */
.video-container {
width: 100vw;
height: 100vh;
background: #000000; margin-top: 80rpx;
position: relative;
overflow: hidden;
}
/* AI生成内容标识 */
.ai-notice {
position: fixed;
right: 12px;width: 100px;
height: 30px; top: 92px;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 12rpx 24rpx;
background: rgba(0, 0, 0, 0.3);
border: 1rpx solid rgba(66, 202, 77, 0.5);
border-radius: 40rpx;
backdrop-filter: blur(10rpx);
z-index: 100;
}
.ai-notice-icon {
font-size: 24rpx;
}
.ai-notice-text {
font-size: 20rpx;
color: #42ca4d;
line-height: 1.4;
}
.video-player {
width: 100%;
height: 100%;
position: relative;
display: flex;
flex-direction: column;
}
/* 顶部控制栏 */
.top-controls {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 48px;
display: flex;
justify-content: space-between;
align-items: flex-end;
padding: 0 16px 16px;
background: linear-gradient(180deg, rgba(0,0,0,0.6) 0%, transparent 100%);
z-index: 10;
}
.left-controls {
display: flex;
align-items: center;
}
.back-btn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
}
.icon-back {
font-size: 24px;
color: #ffffff;
font-weight: bold;
}
/* 文章详情样式 */
.article-status {
margin-bottom: 16px;
padding: 12px 16px;
background: rgba(0, 0, 0, 0.6);
border-radius: 8px;
}
.loading-indicator {
display: flex;
align-items: center;
justify-content: center;
}
.loading-text {
color: #ffffff;
font-size: 14px;
opacity: 0.8;
}
.error-message {
display: flex;
align-items: center;
justify-content: center;
}
.error-text {
color: #ff6b6b;
font-size: 14px;
}
.article-detail {
margin-bottom: 16px;
padding: 16px;
background: rgba(0, 0, 0, 0.6);
border-radius: 12px;
backdrop-filter: blur(10px);
}
.article-header {
margin-bottom: 12px;
}
.article-title {
color: #ffffff;
font-size: 18px;
font-weight: 600;
line-height: 1.4;
}
.article-meta {
display: flex;
align-items: center;
margin-bottom: 12px;
gap: 12px;
}
.publish-time {
color: #ffffff;
font-size: 12px;
opacity: 0.7;
}
.article-category {
color: #4CAF50;
font-size: 12px;
background: rgba(76, 175, 80, 0.2);
padding: 2px 8px;
border-radius: 10px;
}
.article-content {
margin-bottom: 12px;
}
.content-text {
color: #ffffff;
font-size: 14px;
line-height: 1.6;
opacity: 0.9;
}
.article-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tag-item {
color: #2196F3;
font-size: 12px;
background: rgba(33, 150, 243, 0.2);
padding: 4px 8px;
border-radius: 12px;
}
.time-display {
font-size: 14px;
color: #ffffff;
font-weight: 500;
}
.right-controls {
display: flex;
align-items: center;
}
.signal-icons {
display: flex;
align-items: center;
margin-right: 12px;
}
.signal, .wifi, .battery {
font-size: 14px;
color: #ffffff;
margin-left: 4px;
}
.share-btn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.icon-share {
font-size: 18px;
color: #ffffff;
font-weight: bold;
}
/* 视频内容区域 */
.video-content {
flex: 1;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.video-element {
width: 100%;
height: 100%;
object-fit: cover;
}
.play-indicator {
position: absolute;
width: 80px;
height: 80px;
background: rgba(0, 0, 0, 0.6);
border-radius: 40px;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.3s ease;
backdrop-filter: blur(10px);
}
.play-indicator.hide {
opacity: 0;
pointer-events: none;
}
.play-indicator.show {
opacity: 1;
}
.play-icon {
font-size: 32px;
color: #ffffff;
margin-left: 4px;
}
/* 底部信息区域 */
.bottom-info {
position: absolute;
bottom: 60rpx;
left: 0;
right: 0;
padding: 11px;
background: linear-gradient(0deg, rgba(0,0,0,0.8) 0%, transparent 100%);
z-index: 10;
}
/* 创作者信息 */
.creator-info {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.creator-avatar {
width: 40px;
height: 40px;
border-radius: 20px;
margin-right: 12px;
border: 2px solid rgba(255, 255, 255, 0.3);
}
.creator-details {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
}
.creator-name {
font-size: 16px;
color: #ffffff;
font-weight: 500;
}
.follow-btn {
padding: 6px 16px;
background: #ffffff;
color: #333333;
border-radius: 16px;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
}
.follow-btn.following {
background: rgba(255, 255, 255, 0.3);
color: #ffffff;
}
/* 视频描述信息 */
.video-info {
margin-bottom: 16px;
}
.info-tags {
display: flex;
gap: 8px;
margin-bottom: 8px;
}
.tag {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.tag-info {
background: rgba(79, 172, 254, 0.8);
color: #ffffff;
}
.tag-ai {
background: rgba(118, 75, 162, 0.8);
color: #ffffff;
}
.video-description {
font-size: 14px;
color: #ffffff;
line-height: 1.4;
opacity: 0.9;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
cursor: pointer;
}
.video-description.expanded {
-webkit-line-clamp: unset;
line-clamp: unset;
overflow: visible;
}
/* 展开/收起切换按钮 */
.expand-toggle {
display: flex;
justify-content: center;
align-items: center;
margin-top: 8px;
padding: 4px 0;
cursor: pointer;
}
.expand-toggle .iconfont {
font-size: 16px;
color: rgba(255, 255, 255, 0.7);
transition: all 0.3s ease;
}
.expand-toggle:active .iconfont {
color: rgba(255, 255, 255, 1);
transform: scale(1.1);
}
/* 互动操作区 */
.interaction-area {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.like-section {
display: flex;
align-items: center;
}
.like-btn {
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 8px;
transition: transform 0.2s ease;
}
.like-btn:active {
transform: scale(1.2);
}
.heart-icon {
font-size: 24px;
}
.like-count {
font-size: 16px;
color: #ffffff;
font-weight: 500;
}
.action-buttons {
display: flex;
align-items: center;
gap: 12px;
}
/* 悬浮操作按钮 */
.floating-actions {
position: fixed;
right: 16px;
bottom: 35%;
display: flex;
flex-direction: column;
gap: 20px;
z-index: 100;
}
.floating-btn {
width: 42px;
height: 42px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
background: linear-gradient(135deg, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0.25) 100%);
border: 2px solid rgba(255, 255, 255, 0.15);
border-radius: 50%;
backdrop-filter: blur(20px);
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4),
0 4px 8px rgba(0, 0, 0, 0.2);
}
.floating-btn:active {
transform: scale(0.92);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3),
0 2px 4px rgba(0, 0, 0, 0.2);
}
/* 下载按钮 */
.download-btn {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.2) 0%, rgba(56, 142, 60, 0.4) 100%);
border-color: rgba(139, 195, 74, 0.3);
}
.download-btn:active {
background: linear-gradient(135deg, rgba(76, 175, 80, 1) 0%, rgba(56, 142, 60, 0.9) 100%);
}
/* 分享按钮 */
.share-btn {
background: linear-gradient(135deg, rgba(33, 150, 243, 0.2) 0%, rgba(25, 118, 210, 0.4) 100%);
border-color: rgba(100, 181, 246, 0.3);
}
.share-btn:active {
background: linear-gradient(135deg, rgba(33, 150, 243, 1) 0%, rgba(25, 118, 210, 0.9) 100%);
}
/* CSS绘制的下载图标 */
.icon-download {
width: 20px;
height: 20px;
position: relative;
}
.icon-download::before {
content: '';
position: absolute;
left: 50%;
top: 0;
width: 3px;
height: 14px;
background: #ffffff;
transform: translateX(-50%);
border-radius: 2px;
}
.icon-download::after {
content: '';
position: absolute;
left: 50%;
bottom: 0;
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 6px solid #ffffff;
transform: translateX(-50%);
}
/* CSS绘制的分享图标 */
.icon-share {
width: 20px;
height: 20px;
position: relative;
}
.icon-share::before {
content: '';
position: absolute;
left: 50%;
bottom: 0;
width: 3px;
height: 14px;
background: #ffffff;
transform: translateX(-50%);
border-radius: 2px;
}
.icon-share::after {
content: '';
position: absolute;
left: 50%;
top: 0;
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 6px solid #ffffff;
transform: translateX(-50%);
}
.floating-label {
font-size: 12px;
color: #ffffff;
font-weight: 500;
letter-spacing: 0.3px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.consult-btn {
padding: 12px 24px;
background: rgba(255, 255, 255, 0.15);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 24px;
font-size: 14px;
font-weight: 500;
transition: transform 0.2s ease, background 0.2s ease;
}
.consult-btn:active {
transform: scale(0.95);
background: rgba(255, 255, 255, 0.25);
}
.action-btn {
padding: 12px 24px;
background: #ffffff;
color: #333333;
border-radius: 24px;
font-size: 14px;
font-weight: 500;
transition: transform 0.2s ease;
}
.action-btn:active {
transform: scale(0.95);
}
/* 进度条 */
.progress-bar {
width: 100%;
}
.progress-track {
width: 100%;
height: 2px;
background: rgba(255, 255, 255, 0.3);
border-radius: 1px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #ffffff;
border-radius: 1px;
transition: width 0.1s ease;
}
/* 点赞动画 */
@keyframes likeAnimation {
0% {
transform: scale(1);
}
50% {
transform: scale(1.3);
}
100% {
transform: scale(1);
}
}
.like-btn.liked .heart-icon {
animation: likeAnimation 0.6s ease;
}
</style>