918 lines
21 KiB
Vue
918 lines
21 KiB
Vue
<template>
|
||
<view class="navbox">
|
||
<view class="status_bar"></view>
|
||
<uni-nav-bar
|
||
left-icon="left"
|
||
title="视频缓存"
|
||
@clickLeft="goBack"
|
||
color="#8B2316"
|
||
:border="false"
|
||
backgroundColor="#eeeeee"
|
||
>
|
||
<template #right>
|
||
<view class="nav-actions">
|
||
<view class="nav-edit" @click="toggleEdit">
|
||
<text>{{ isEditMode ? "取消" : "编辑" }}</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</uni-nav-bar>
|
||
</view>
|
||
<view class="download-video-page">
|
||
<!-- 标签页 -->
|
||
<view class="tabs-container">
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'completed' }"
|
||
@click="switchTab('completed')"
|
||
>
|
||
已完成
|
||
</view>
|
||
<view class="tab-divider"></view>
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === 'downloading' }"
|
||
@click="switchTab('downloading')"
|
||
>
|
||
缓存中
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 内容列表 -->
|
||
<scroll-view class="content-scroll" scroll-y>
|
||
<view v-if="currentList.length === 0" class="empty-state">
|
||
<text>暂无数据</text>
|
||
</view>
|
||
|
||
<view
|
||
v-for="(item, index) in currentList"
|
||
:key="item.id || index"
|
||
class="video-item"
|
||
@click="handleItemClick(item)"
|
||
>
|
||
<!-- 编辑模式下的多选框 -->
|
||
<view
|
||
v-if="isEditMode"
|
||
class="checkbox-wrapper"
|
||
@click.stop="toggleSelectItem(item)"
|
||
>
|
||
<view
|
||
class="checkbox"
|
||
:class="{ checked: isItemSelected(item) }"
|
||
>
|
||
<uni-icons
|
||
v-if="isItemSelected(item)"
|
||
type="checkmarkempty"
|
||
size="16"
|
||
color="#ffffff"
|
||
></uni-icons>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 缩略图 -->
|
||
<image
|
||
class="video-thumbnail"
|
||
:src="docUrl + item.imgpath"
|
||
mode="aspectFill"
|
||
/>
|
||
|
||
<!-- 内容信息 -->
|
||
<view class="video-info">
|
||
<view class="video-title">{{ item.name || "视频标题" }}</view>
|
||
<view class="video-author">{{ item.author || "作者" }}</view>
|
||
|
||
<!-- 时长和大小 -->
|
||
<view class="video-meta" v-if="item.status === 'completed'">
|
||
<uni-icons
|
||
type="clock"
|
||
size="12"
|
||
color="#999999"
|
||
class="meta-icon"
|
||
></uni-icons>
|
||
<text class="meta-text"><text v-if="item.duration">{{ formatDuration(item.duration) }}</text></text>
|
||
<text class="meta-size">{{ formatSize(item.size) }}</text>
|
||
</view>
|
||
<view class="video-meta video-status" v-if="item.status == 'downloading' || item.status == 'paused'">
|
||
<view class="status-icon">
|
||
<image :src="item.status == 'paused' ? playImg : pauseImg" mode="aspectFill"></image>
|
||
<text>{{ item.status == 'paused' ? '继续' : '暂停' }}</text>
|
||
</view>
|
||
<view class="box">
|
||
<!-- <view class="video-progress-bar">
|
||
<view class="video-progress-bar-inner" :style="{ width: item.progress + '%' }"></view>
|
||
</view> -->
|
||
<!-- <text>{{ item.progress + '%' }}</text> -->
|
||
<view class="size-box">{{ formatSize(item.downloadSize) }}/{{ formatSize(item.totalSize) }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 编辑模式下的删除按钮 -->
|
||
<!-- <view
|
||
v-if="isEditMode"
|
||
class="delete-btn"
|
||
@click.stop="deleteItem(item, index)"
|
||
>
|
||
<text>删除</text>
|
||
</view> -->
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部存储信息 -->
|
||
<view class="bottom-box">
|
||
<view class="storage-info">
|
||
<text
|
||
>手机存储:总空间{{ storageInfo.total }}/剩余{{
|
||
storageInfo.available
|
||
}}可用</text
|
||
>
|
||
</view>
|
||
<view class="footer-btn" v-if="isEditMode">
|
||
<view class="footer-btn-item select" @click="clearAll">
|
||
<text v-if="!isAllSelect">全选</text>
|
||
<text v-else>取消全选</text>
|
||
</view>
|
||
<view class="bar"></view>
|
||
<view class="footer-btn-item del" @click="delTask">
|
||
删除<text v-if="selectedItems.length > 0">({{ selectedItems.length }})</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted, nextTick } from "vue";
|
||
import downloadStore from "@/store/downloadStoreVideo.js";
|
||
import { onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
||
import playImg from "@/static/down_true.png";
|
||
import pauseImg from "@/static/down_false.png";
|
||
import navTo from "@/utils/navTo";
|
||
import docUrl from "@/utils/docUrl";
|
||
const downloadTasks = ref([]);
|
||
const unsubscribe = ref(null);
|
||
const isAllSelect = ref(false);
|
||
const selectedItems = ref([]);
|
||
// 响应式数据
|
||
const activeTab = ref("completed"); // completed: 已完成, downloading: 缓存中
|
||
const isEditMode = ref(false);
|
||
const storageInfo = ref({
|
||
total: "461.31G",
|
||
available: "312.92G",
|
||
});
|
||
const delTask=()=>{
|
||
if(selectedItems.value.length === 0){
|
||
uni.showToast({
|
||
title: "请选择要删除的缓存",
|
||
icon: "none",
|
||
});
|
||
return;
|
||
}
|
||
uni.showModal({
|
||
title: "提示",
|
||
content: "确定要删除选中的缓存吗?",
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 从后往前删除,避免索引变化问题
|
||
const idsToDelete = [...selectedItems.value];
|
||
idsToDelete.forEach(id => {
|
||
const taskIndex = downloadTasks.value.findIndex(t => t.id === id);
|
||
if (taskIndex !== -1) {
|
||
removeTask(taskIndex);
|
||
}
|
||
});
|
||
// 清空选择
|
||
selectedItems.value = [];
|
||
isAllSelect.value = false;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
const clearAll=()=>{
|
||
if(!isAllSelect.value){
|
||
// 全选当前列表的所有项
|
||
selectedItems.value = currentList.value.map(item => item.id);
|
||
isAllSelect.value = true;
|
||
}else{
|
||
// 取消全选
|
||
selectedItems.value = [];
|
||
isAllSelect.value = false;
|
||
}
|
||
}
|
||
|
||
// 判断项是否被选中
|
||
const isItemSelected = (item) => {
|
||
return selectedItems.value.includes(item.id);
|
||
}
|
||
|
||
// 切换项的选中状态
|
||
const toggleSelectItem = (item) => {
|
||
const index = selectedItems.value.indexOf(item.id);
|
||
if (index > -1) {
|
||
selectedItems.value.splice(index, 1);
|
||
} else {
|
||
selectedItems.value.push(item.id);
|
||
}
|
||
// 更新全选状态
|
||
isAllSelect.value = selectedItems.value.length === currentList.value.length && currentList.value.length > 0;
|
||
}
|
||
|
||
// 计算当前显示的列表
|
||
const currentList = computed(() => {
|
||
let list = [];
|
||
if (activeTab.value === "completed") {
|
||
list = downloadTasks.value.filter((task) => task.status === "completed");
|
||
} else {
|
||
list = downloadTasks.value.filter(
|
||
(task) => task.status === "downloading" || task.status === "paused"
|
||
);
|
||
}
|
||
// 当列表变化时,更新全选状态
|
||
nextTick(() => {
|
||
if (isAllSelect.value && selectedItems.value.length !== list.length) {
|
||
isAllSelect.value = false;
|
||
}
|
||
});
|
||
return list;
|
||
});
|
||
// 方法
|
||
const goBack = () => {
|
||
uni.navigateBack({
|
||
fail() {
|
||
uni.redirectTo({
|
||
url: "/pages/index/index",
|
||
});
|
||
},
|
||
});
|
||
};
|
||
|
||
const switchTab = (tab) => {
|
||
activeTab.value = tab;
|
||
isEditMode.value = false;
|
||
// 切换标签时清空选择
|
||
selectedItems.value = [];
|
||
isAllSelect.value = false;
|
||
};
|
||
|
||
const toggleEdit = () => {
|
||
isEditMode.value = !isEditMode.value;
|
||
// 退出编辑模式时清空选择
|
||
if (!isEditMode.value) {
|
||
selectedItems.value = [];
|
||
isAllSelect.value = false;
|
||
}
|
||
};
|
||
|
||
const handleItemClick = (item) => {
|
||
if (isEditMode.value) {
|
||
// 编辑模式下,点击项切换选中状态
|
||
toggleSelectItem(item);
|
||
return;
|
||
}
|
||
// 跳转到视频详情或播放页面
|
||
if (item.status === "completed") {
|
||
uni.navigateTo({
|
||
url: `/pages_app/videoDetail/videoDetail?id=${item.id}&from=download`,
|
||
});
|
||
}else if(item.status === "downloading"){
|
||
let index=downloadTasks.value.findIndex(t => t.id === item.id);
|
||
if(index !== -1){
|
||
pauseTask(index);
|
||
}
|
||
}else if(item.status === "paused"){
|
||
let index=downloadTasks.value.findIndex(t => t.id === item.id);
|
||
if(index !== -1){
|
||
resumeTask(index);
|
||
}
|
||
}
|
||
};
|
||
|
||
const deleteItem = (item, index) => {
|
||
uni.showModal({
|
||
title: "提示",
|
||
content: "确定要删除这个缓存吗?",
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// 从store中删除
|
||
const taskIndex = downloadTasks.value.findIndex((t) => t.id === item.id);
|
||
if (taskIndex !== -1) {
|
||
removeTask(taskIndex);
|
||
|
||
}
|
||
}
|
||
},
|
||
});
|
||
};
|
||
|
||
const formatDuration = (duration) => {
|
||
if (!duration) return "00:00:00";
|
||
// 如果duration是秒数
|
||
if (typeof duration === "number") {
|
||
const hours = Math.floor(duration / 3600);
|
||
const minutes = Math.floor((duration % 3600) / 60);
|
||
const seconds = duration % 60;
|
||
return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(
|
||
2,
|
||
"0"
|
||
)}:${String(seconds).padStart(2, "0")}`;
|
||
}
|
||
// 如果已经是格式化字符串
|
||
return duration;
|
||
};
|
||
|
||
const formatSize = (size) => {
|
||
if (!size) return "0M";
|
||
// 如果size是字节数
|
||
|
||
const mb = (Number(size) / 1024 / 1024).toFixed(2);
|
||
return `${mb}M`;
|
||
|
||
// 如果已经是格式化字符串
|
||
return size;
|
||
};
|
||
|
||
onLoad(() => {
|
||
// 从store同步任务列表
|
||
syncTasksFromStore();
|
||
// 监听store变化
|
||
unsubscribe.value = downloadStore.addListener((tasks) => {
|
||
downloadTasks.value = tasks;
|
||
});
|
||
console.log(11111);
|
||
console.log(downloadTasks.value);
|
||
// 恢复正在下载的任务
|
||
resumeDownloadingTasks();
|
||
}),
|
||
onShow(() => {
|
||
// 页面显示时同步最新任务列表
|
||
syncTasksFromStore();
|
||
// 检查并恢复下载任务
|
||
resumeDownloadingTasks();
|
||
}),
|
||
onUnload(() => {
|
||
// 页面 卸载时取消监听
|
||
if (unsubscribe.value) {
|
||
unsubscribe.value();
|
||
}
|
||
});
|
||
|
||
const getStorageInfo = () => {
|
||
// 获取手机存储信息
|
||
// #ifdef APP-PLUS
|
||
if (uni.getSystemInfoSync().platform !== 'android') {
|
||
uni.sendNativeEvent('getSysSpace',{
|
||
msg: 'getSysSpace'
|
||
},(res) => {
|
||
storageInfo.value = {
|
||
total: res.total,
|
||
available: res.available,
|
||
};
|
||
});
|
||
}else{
|
||
// 1. 导入所需的Android类
|
||
const Environment = plus.android.importClass("android.os.Environment");
|
||
const StatFs = plus.android.importClass("android.os.StatFs");
|
||
|
||
try {
|
||
// 2. 获取内部存储的数据目录(File对象)
|
||
const dataDirectory = Environment.getDataDirectory(); // 这是一个Java File对象
|
||
|
||
// 3. 关键修正:使用 plus.android.invoke 调用 File 对象的 getPath 方法
|
||
const dataDirectoryPath = plus.android.invoke(dataDirectory, "getPath");
|
||
// 现在 dataDirectoryPath 是一个字符串,表示路径
|
||
|
||
// 4. 创建 StatFs 对象,传入路径字符串
|
||
const statFs = new StatFs(dataDirectoryPath);
|
||
|
||
// 5. 使用 invoke 调用 StatFs 的方法获取存储块信息
|
||
const blockSize = plus.android.invoke(statFs, "getBlockSize");
|
||
const availableBlocks = plus.android.invoke(statFs, "getAvailableBlocks");
|
||
const totalBlocks = plus.android.invoke(statFs, "getBlockCount");
|
||
|
||
|
||
// 6. 计算可用空间和总空间(单位:字节)
|
||
const availableSizeInBytes = blockSize * availableBlocks;
|
||
const totalSizeInBytes = blockSize * totalBlocks;
|
||
|
||
|
||
// 7. 转换为常用单位
|
||
const availableSizeInGB = (availableSizeInBytes / (1024 * 1024 * 1024)).toFixed(2);
|
||
const availableSizeInMB = (availableSizeInBytes / (1024 * 1024)).toFixed(2);
|
||
const totalSizeInGB = (totalSizeInBytes / (1024 * 1024 * 1024)).toFixed(2);
|
||
|
||
|
||
// console.log(`可用存储空间:${availableSizeInBytes} 字节`);
|
||
// console.log(`可用存储空间:${availableSizeInMB} MB`);
|
||
// console.log(`可用存储空间:${availableSizeInGB} GB`);
|
||
|
||
// 可以根据需要返回不同单位的值
|
||
storageInfo.value = {
|
||
total: totalSizeInGB+"G",
|
||
available: availableSizeInGB+"G",
|
||
};
|
||
|
||
} catch (error) {
|
||
console.error('获取存储信息时发生错误:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// #endif
|
||
};
|
||
const syncTasksFromStore=()=> {
|
||
downloadTasks.value = downloadStore.getTasks();
|
||
};
|
||
// 添加下载任务
|
||
const addDownloadTask = (item) => {
|
||
// 使用store添加任务
|
||
const taskIndex = downloadStore.addTask(item);
|
||
|
||
// 同步任务列表
|
||
syncTasksFromStore();
|
||
|
||
// 开始下载
|
||
startDownload(taskIndex);
|
||
};
|
||
|
||
// 开始下载
|
||
const startDownload = (index) => {
|
||
const taskItem = downloadStore.getTask(index);
|
||
if (!taskItem) return;
|
||
|
||
const task = uni.downloadFile({
|
||
url: taskItem.url,
|
||
success: (res) => {
|
||
if (res.statusCode === 200) {
|
||
downloadStore.updateTask(index, {
|
||
status: "completed",
|
||
filePath: res.tempFilePath,
|
||
});
|
||
uni.showToast({
|
||
title: "下载成功",
|
||
icon: "success",
|
||
});
|
||
|
||
console.log(res);
|
||
uni.saveFile({
|
||
tempFilePath: res.tempFilePath,
|
||
success: function (res) {
|
||
uni.getSavedFileInfo({
|
||
filePath: res.savedFilePath,
|
||
success: function (res) {
|
||
downloadStore.updateTask(index, {
|
||
status: "completed",
|
||
realyPath: res.savedFilePath,
|
||
size: res.size,
|
||
});
|
||
},
|
||
});
|
||
},
|
||
});
|
||
} else {
|
||
downloadStore.updateTask(index, {
|
||
status: "failed",
|
||
});
|
||
uni.showToast({
|
||
title: "下载失败",
|
||
icon: "none",
|
||
});
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
downloadStore.updateTask(index, {
|
||
status: "failed",
|
||
});
|
||
console.error("下载失败:", err);
|
||
uni.showToast({
|
||
title: "下载失败: " + (err.errMsg || "未知错误"),
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
},
|
||
});
|
||
|
||
|
||
// 监听下载进度
|
||
task.onProgressUpdate((update) => {
|
||
const currentTask = downloadStore.getTask(index);
|
||
if (currentTask && currentTask.status === "downloading") {
|
||
console.log(update.progress);
|
||
downloadStore.updateTask(index, {
|
||
progress: update.progress,
|
||
downloadSize: update.totalBytesWritten,
|
||
totalSize: update.totalBytesExpectedToWrite,
|
||
});
|
||
}
|
||
});
|
||
|
||
// 保存task对象到store(用于暂停/继续)
|
||
downloadStore.updateTask(index, {
|
||
task: task,
|
||
});
|
||
};
|
||
|
||
// 暂停任务
|
||
const pauseTask = (index) => {
|
||
const taskItem = downloadStore.getTask(index);
|
||
if (taskItem && taskItem.task) {
|
||
taskItem.task.abort();
|
||
downloadStore.updateTask(index, {
|
||
status: "paused",
|
||
task: null,
|
||
});
|
||
uni.showToast({
|
||
title: "已暂停",
|
||
icon: "none",
|
||
});
|
||
}
|
||
};
|
||
|
||
// 继续任务
|
||
const resumeTask = (index) => {
|
||
downloadStore.updateTask(index, {
|
||
status: "downloading",
|
||
progress: 0, // 重新开始(uni.downloadFile不支持断点续传)
|
||
});
|
||
startDownload(index);
|
||
};
|
||
|
||
// 删除任务
|
||
const removeTask = (index) => {
|
||
downloadStore.removeTask(index);
|
||
syncTasksFromStore();
|
||
};
|
||
|
||
// 打开文件
|
||
const openFile = (filePath) => {
|
||
// #ifdef APP-PLUS
|
||
plus.runtime.openFile(filePath);
|
||
// #endif
|
||
// #ifndef APP-PLUS
|
||
uni.showToast({
|
||
title: "请在APP中打开",
|
||
icon: "none",
|
||
});
|
||
// #endif
|
||
};
|
||
|
||
// 获取文件名
|
||
const getFileName = (url) => {
|
||
if (!url) return "未知文件";
|
||
try {
|
||
const urlObj = new URL(url);
|
||
const pathname = urlObj.pathname;
|
||
const fileName = pathname.split("/").pop();
|
||
return fileName || "下载文件";
|
||
} catch (e) {
|
||
// 如果不是完整URL,尝试从路径中提取
|
||
const parts = url.split("/");
|
||
return parts[parts.length - 1] || "下载文件";
|
||
}
|
||
};
|
||
|
||
// 获取状态文本
|
||
const getStatusText = (status) => {
|
||
const statusMap = {
|
||
downloading: "下载中",
|
||
paused: "已暂停",
|
||
completed: "已完成",
|
||
failed: "下载失败",
|
||
};
|
||
return statusMap[status] || "未知";
|
||
};
|
||
|
||
// 恢复正在下载的任务
|
||
const resumeDownloadingTasks = () => {
|
||
// 使用 nextTick 确保在页面渲染后再恢复下载任务
|
||
nextTick(() => {
|
||
downloadStore.resumeDownloadingTasks((index) => {
|
||
// 重新开始下载(uni.downloadFile不支持断点续传,所以从0开始)
|
||
console.log("恢复下载任务:", downloadStore.getTask(index)?.url);
|
||
startDownload(index);
|
||
});
|
||
});
|
||
};
|
||
|
||
onMounted(() => {
|
||
getStorageInfo();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
page{
|
||
background-color: #ffffff;
|
||
}
|
||
.size-box{
|
||
display: flex;
|
||
white-space: nowrap;
|
||
justify-content: flex-end;
|
||
|
||
white-space: nowrap;
|
||
font-size: 24rpx;
|
||
color:#8b2316
|
||
}
|
||
.box{
|
||
display: flex;
|
||
white-space: nowrap;
|
||
justify-content: flex-end;
|
||
margin-left: 30rpx;
|
||
text{
|
||
width: 40rpx;
|
||
display: flex;
|
||
white-space: nowrap;
|
||
font-size: 24rpx;
|
||
color:#8b2316
|
||
}
|
||
}
|
||
.download-video-page {
|
||
width: 100%;
|
||
margin-top: calc(var(--status-bar-height) + 44px);
|
||
height: calc(100vh - var(--status-bar-height) - 44px);
|
||
background-color: #ffffff;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.footer-btn{
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
|
||
height: 100rpx;
|
||
background-color: #ffffff;
|
||
border-top: 1px solid #f0f0f0;
|
||
display: flex;
|
||
justify-content: center;
|
||
.select{
|
||
flex:1;
|
||
height: 100rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #333;
|
||
}
|
||
.del{
|
||
height: 100rpx;
|
||
flex:1;
|
||
color:red;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.bar{
|
||
width: 1px;
|
||
height: 40rpx;
|
||
|
||
background-color: #999;
|
||
}
|
||
}
|
||
.video-progress-bar {
|
||
flex:1;
|
||
height: 10rpx;
|
||
background-color: #f0f0f0;
|
||
border-radius: 5px;
|
||
}
|
||
.video-progress-bar-inner {
|
||
height: 10rpx;
|
||
background-color: #8b2316;
|
||
border-radius: 5px;
|
||
}
|
||
.nav-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 44px;
|
||
padding: 0 15px;
|
||
background-color: #ffffff;
|
||
position: relative;
|
||
}
|
||
|
||
.nav-back {
|
||
width: 40px;
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.back-icon {
|
||
font-size: 28px;
|
||
color: #8b2316;
|
||
font-weight: bold;
|
||
line-height: 1;
|
||
}
|
||
|
||
.nav-title {
|
||
position: absolute;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
font-size: 18px;
|
||
font-weight: 500;
|
||
color: #8b2316;
|
||
}
|
||
|
||
.nav-edit {
|
||
width: 40px;
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
color: #333333;
|
||
}
|
||
|
||
.tabs-container {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 50px;
|
||
background-color: #ffffff;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
position: sticky;
|
||
top: calc(var(--status-bar-height) + 44px);
|
||
z-index: 100;
|
||
}
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
height: 50px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
color: #999999;
|
||
position: relative;
|
||
|
||
&.active {
|
||
color: #8b2316;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.tab-divider {
|
||
width: 1px;
|
||
height: 20px;
|
||
background-color: #e0e0e0;
|
||
}
|
||
|
||
.content-scroll {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding-bottom: 200rpx;
|
||
background-color: #ffffff;
|
||
}
|
||
|
||
.empty-state {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 100px 0;
|
||
color: #999999;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.video-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 15px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
position: relative;
|
||
}
|
||
|
||
.video-thumbnail {
|
||
width: 120px;
|
||
height: 80px;
|
||
border-radius: 4px;
|
||
background-color: #f5f5f5;
|
||
margin-right: 12px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.video-info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
min-height: 80px;
|
||
}
|
||
|
||
.video-title {
|
||
font-size: 15px;
|
||
color: #333333;
|
||
line-height: 1.4;
|
||
margin-bottom: 4px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.video-author {
|
||
font-size: 13px;
|
||
color: #666666;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.video-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 12px;
|
||
color: #999999;
|
||
}
|
||
.video-status{
|
||
display: flex;
|
||
|
||
|
||
justify-content: space-between;
|
||
.status-icon{
|
||
display: flex;
|
||
align-items: center;
|
||
white-space: nowrap;
|
||
justify-content: center;
|
||
image{
|
||
width:30rpx;
|
||
height: 29rpx;
|
||
margin-right: 10rpx;
|
||
}
|
||
text{
|
||
white-space: nowrap;
|
||
font-size: 24rpx;
|
||
color: #999999;
|
||
}
|
||
}
|
||
}
|
||
|
||
.meta-icon {
|
||
margin-right: 4px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.meta-text {
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.meta-size {
|
||
margin-left: auto;
|
||
}
|
||
|
||
.checkbox-wrapper {
|
||
margin-right: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.checkbox {
|
||
width: 20px;
|
||
height: 20px;
|
||
border: 1px solid #d0d0d0;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: #ffffff;
|
||
transition: all 0.2s;
|
||
|
||
&.checked {
|
||
background-color: #8b2316;
|
||
border-color: #8b2316;
|
||
}
|
||
}
|
||
|
||
.delete-btn {
|
||
position: absolute;
|
||
right: 15px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
padding: 8px 16px;
|
||
background-color: #ff4444;
|
||
color: #ffffff;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.bottom-box {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
width: 100%;
|
||
background-color: #ffffff;
|
||
z-index: 100;
|
||
}
|
||
|
||
.storage-info {
|
||
padding: 12px 15px;
|
||
background-color: #f8f8f8;
|
||
border-top: 1px solid #f0f0f0;
|
||
font-size: 12px;
|
||
color: #666666;
|
||
text-align: center;
|
||
}
|
||
</style>
|
||
|