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

1374 lines
32 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="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>