新增工作区下载列表

This commit is contained in:
2026-08-03 09:05:21 +08:00
parent 314a714248
commit e057e33601
7 changed files with 1416 additions and 31 deletions

View File

@@ -380,3 +380,164 @@
.workspace-footer .iconfont {
font-size: 40rpx !important;
}
/* ===== 标签切换 ===== */
.workspace-tabs {
display: flex;
width: 100%;
padding: 0 24rpx;
box-sizing: border-box;
gap: 20rpx;
}
.tab-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 18rpx 0;
font-size: 28rpx;
font-weight: 500;
color: #999;
border-bottom: 4rpx solid transparent;
transition: all 0.2s ease;
position: relative;
}
.tab-item.active {
color: #6c63ff;
border-bottom-color: #6c63ff;
}
.tab-badge {
margin-left: 8rpx;
background: #ff5e57;
color: #fff;
font-size: 20rpx;
padding: 2rpx 10rpx;
border-radius: 20rpx;
line-height: 1.4;
}
/* ===== 下载列表 ===== */
.download-content {
flex: 1;
overflow: auto;
}
.download-empty {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-top: 200rpx;
color: #bbb;
font-size: 28rpx;
}
.download-empty .iconfont {
font-size: 140rpx;
color: #ddd;
margin-bottom: 20rpx;
}
.download-list {
padding: 20rpx 24rpx;
}
.download-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12rpx 8rpx 20rpx;
font-size: 24rpx;
color: #999;
}
.clear-btn {
color: #ff5e57;
font-size: 24rpx;
font-weight: 500;
padding: 4rpx 12rpx;
}
.download-item {
display: flex;
align-items: center;
padding: 24rpx 20rpx;
background: rgba(255, 255, 255, 0.85);
border-radius: 20rpx;
margin-bottom: 16rpx;
transition: all 0.15s ease;
}
.download-item:active {
transform: scale(0.98);
background: rgba(255, 255, 255, 0.95);
}
.di-icon {
width: 80rpx;
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
background: rgba(108, 99, 255, 0.08);
border-radius: 18rpx;
margin-right: 20rpx;
flex-shrink: 0;
}
.di-icon .iconfont {
font-size: 40rpx;
color: #6c63ff;
}
.di-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 6rpx;
min-width: 0;
}
.di-name {
font-size: 28rpx;
font-weight: 500;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.di-meta {
font-size: 22rpx;
color: #999;
}
.di-path {
font-size: 20rpx;
color: #bbb;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.di-delete {
width: 56rpx;
height: 56rpx;
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
}
.di-delete .iconfont {
font-size: 36rpx;
color: #ccc;
transition: color 0.15s ease;
}
.di-delete:active .iconfont {
color: #ff5e57;
}

View File

@@ -66,8 +66,20 @@
<view v-if="isSearchFocus" class="cancel-search" @click="handleSearchFocus">取消</view>
</view>
<!-- 标签切换 -->
<view class="workspace-tabs">
<view class="tab-item" :class="{ active: activeTab === 'workspace' }" @click="switchTab('workspace')">
工作区
</view>
<view class="tab-item" :class="{ active: activeTab === 'downloads' }" @click="switchTab('downloads')">
下载列表
<text v-if="downloadList.length > 0" class="tab-badge">{{ downloadList.length }}</text>
</view>
</view>
</view>
<view class="workspace-content">
<!-- ===== 工作区文件 ===== -->
<view class="workspace-content" v-if="activeTab === 'workspace'">
<view class="folder-null" v-if="currentFolderList.length===0">
<view class="iconfont icon-wenjianjia"></view>
文件夹为空
@@ -89,6 +101,34 @@
</view>
</view>
</view>
<!-- ===== 下载列表 ===== -->
<view class="download-content" v-if="activeTab === 'downloads'">
<view v-if="downloadList.length === 0" class="download-empty">
<text class="iconfont icon-xiazai"></text>
<text>暂无下载文件</text>
</view>
<view v-else class="download-list">
<view class="download-header">
<text>已下载 {{ downloadList.length }} 个文件</text>
<text class="clear-btn" @click="handleClearDownloads">清空</text>
</view>
<view class="download-item" v-for="item in downloadList" :key="item.id" @click="openDownloadFile(item)">
<view class="di-icon">
<text class="iconfont" :class="getFileIcon(item.name)"></text>
</view>
<view class="di-info">
<text class="di-name">{{ item.name }}</text>
<text class="di-meta">{{ formatDownloadTime(item.time) }} · {{ formatDownloadSize(item.size) }}</text>
<text class="di-path">{{ item.displayPath || item.path }}</text>
</view>
<view class="di-delete" @click.stop="handleDeleteDownload(item.id)">
<text class="iconfont icon-del"></text>
</view>
</view>
</view>
</view>
<view v-if="isSelectFolder" class="workspace-footer" :class="{'folder-actions':selectFileList.length>0}">
<view class="iconfont icon-shangchuan" @click="handleUploadFile"></view>
<!-- <view class="iconfont icon-fuzhi"></view> -->
@@ -122,11 +162,21 @@
updateWorkspaceFileName
} from '@/utils/cloud-api.js'
import { openFile as openFileNative } from '@/utils/fileOpener.js'
import {
getDownloadList,
saveDownload,
deleteDownload,
clearAllDownloads,
formatFileSize,
formatTime
} from '@/utils/downloadManager.js'
const UserToken = ref('')
const workspaceId = ref('')
const navbarBeforeTitle = ref('');
const navbarTitle = ref('工作区');
const activeTab = ref('workspace')
const downloadList = ref([])
// 选择文件模式
const isSelectFolder = ref(false);
@@ -395,6 +445,7 @@ import { openFile as openFileNative } from '@/utils/fileOpener.js'
const downloadUrl = fileInfo.url;
const fileName = folder.name;
const fileSize = fileInfo.size || 0;
uni.showLoading({
title: '下载中...'
@@ -407,8 +458,14 @@ import { openFile as openFileNative } from '@/utils/fileOpener.js'
uni.hideLoading();
if (downloadRes.statusCode === 200) {
const tempPath = downloadRes.tempFilePath;
// 下载成功,直接打开文件
console.log("// 下载成功,文件下载路径为:",tempPath);
// 持久化保存到本地
saveDownload(tempPath, fileName, fileSize).then((record) => {
downloadList.value.unshift(record);
uni.showToast({ title: '下载成功', icon: 'success' });
}).catch(() => {
// 保存失败不影响打开
});
// 下载成功后打开文件
openFile(tempPath);
} else {
uni.showToast({
@@ -443,6 +500,102 @@ import { openFile as openFileNative } from '@/utils/fileOpener.js'
});
};
// ===== 下载列表相关 =====
const switchTab = (tab) => {
activeTab.value = tab
if (tab === 'downloads') {
loadDownloads()
}
}
const loadDownloads = () => {
downloadList.value = getDownloadList()
}
const openDownloadFile = (item) => {
// #ifdef APP-PLUS
// 检查公共目录下文件是否存在
plus.io.resolveLocalFileSystemURL(
item.path,
() => {
// 文件存在,直接打开
openFile(item.path)
},
() => {
// 文件已被手动删除
uni.showToast({ title: '文件已失效,将移除记录', icon: 'none' })
handleDeleteDownload(item.id)
}
)
// #endif
// #ifndef APP-PLUS
uni.getSavedFileList({
success: (res) => {
const found = res.fileList.find(f => f.filePath === item.path)
if (found) {
openFile(item.path)
} else {
uni.showToast({ title: '文件已失效,将移除记录', icon: 'none' })
handleDeleteDownload(item.id)
}
},
fail: () => openFile(item.path)
})
// #endif
}
const handleDeleteDownload = (id) => {
uni.showModal({
title: '删除文件',
content: '确定要删除此下载文件吗?',
success: (res) => {
if (res.confirm) {
deleteDownload(id).then(() => {
downloadList.value = getDownloadList()
uni.showToast({ title: '已删除', icon: 'success' })
}).catch(() => {
uni.showToast({ title: '删除失败', icon: 'error' })
})
}
}
})
}
const handleClearDownloads = () => {
if (downloadList.value.length === 0) return
uni.showModal({
title: '清空下载',
content: '确定要清空所有下载文件吗?此操作不可撤销。',
success: (res) => {
if (res.confirm) {
clearAllDownloads().then(() => {
downloadList.value = []
uni.showToast({ title: '已清空', icon: 'success' })
})
}
}
})
}
const formatDownloadSize = formatFileSize
const formatDownloadTime = formatTime
// const getFileIcon = (fileName) => {
// const ext = fileName.split('.').pop().toLowerCase()
// const iconMap = {
// pdf: 'icon-PDF',
// doc: 'icon-word', docx: 'icon-word',
// xls: 'icon-excel', xlsx: 'icon-excel',
// ppt: 'icon-PPD', pptx: 'icon-PPD',
// zip: 'icon-yasuobao', rar: 'icon-yasuobao', '7z': 'icon-yasuobao',
// jpg: 'icon-tupian', jpeg: 'icon-tupian', png: 'icon-tupian', gif: 'icon-tupian',
// mp3: 'icon-yinle', wav: 'icon-yinle', flac: 'icon-yinle',
// mp4: 'icon-shipin', avi: 'icon-shipin', mov: 'icon-shipin',
// txt: 'icon-txt', md: 'icon-txt',
// }
// return iconMap[ext] || 'icon-weizhiwenjian'
// }
// WYXD 查看器入口
const openWyxdViewer = (folder) => {
const filePath = folder.path.startsWith('./') ? folder.path.slice(2) : folder.path;
@@ -666,6 +819,7 @@ import { openFile as openFileNative } from '@/utils/fileOpener.js'
onMounted(() => {
initCurrentFolderList();
loadDownloads();
})
</script>