新增工作区下载列表

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

284
utils/downloadManager.js Normal file
View File

@@ -0,0 +1,284 @@
/**
* 下载管理工具
* 文件保存到公共下载目录,用户可通过文件管理器访问
*/
const STORAGE_KEY = 'localDownloads'
const SUB_DIR = 'AppDownloads' // 公共下载目录下的子文件夹
/**
* 获取公共下载目录路径
* @returns {Promise<string>} 下载目录绝对路径
*/
function getPublicDownloadDir() {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
plus.io.requestFileSystem(
plus.io.PUBLIC_DOWNLOADS,
(fs) => {
// PUBLIC_DOWNLOADS 对应 /storage/emulated/0/Download/
fs.root.getDirectory(
SUB_DIR,
{ create: true },
(dirEntry) => {
resolve(dirEntry.fullPath)
},
(err) => {
console.error('getDirectory failed:', err)
reject(err)
}
)
},
(err) => {
console.error('requestFileSystem failed:', err)
reject(err)
}
)
// #endif
// #ifndef APP-PLUS
reject(new Error('仅APP端支持'))
// #endif
})
}
/**
* 从完整路径中提取可读的显示路径
* 如 /storage/emulated/0/Download/AppDownloads/xxx.pdf → 下载/AppDownloads/xxx.pdf
*/
function getDisplayPath(fullPath) {
const idx = fullPath.indexOf('/Download/')
if (idx !== -1) {
return '下载/' + fullPath.slice(idx + 10)
}
return fullPath
}
/**
* 获取所有下载记录
* @returns {Array} 下载记录列表
*/
export function getDownloadList() {
try {
return uni.getStorageSync(STORAGE_KEY) || []
} catch (e) {
return []
}
}
/**
* 保存下载记录(复制到公共下载目录)
* @param {string} tempFilePath 临时文件路径uni.downloadFile 返回的)
* @param {string} fileName 文件名
* @param {number} fileSize 文件大小(字节,可选)
*/
export function saveDownload(tempFilePath, fileName, fileSize) {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
getPublicDownloadDir().then((dirPath) => {
const destPathForUrl = plus.io.convertLocalFileSystemURL(dirPath + '/' + fileName)
plus.io.resolveLocalFileSystemURL(
tempFilePath,
(fileEntry) => {
plus.io.resolveLocalFileSystemURL(
dirPath,
(dirEntry) => {
fileEntry.copyTo(
dirEntry,
fileName,
() => {
const record = {
id: Date.now().toString(),
name: fileName,
path: destPathForUrl,
displayPath: getDisplayPath(destPathForUrl),
size: fileSize || 0,
time: Date.now()
}
const list = getDownloadList()
const exists = list.some(item => item.name === fileName)
if (exists) {
const idx = list.findIndex(item => item.name === fileName)
if (idx !== -1) list.splice(idx, 1)
}
list.unshift(record)
try {
uni.setStorageSync(STORAGE_KEY, list)
} catch (e) {
list.pop()
uni.setStorageSync(STORAGE_KEY, list)
}
resolve(record)
},
(err) => {
console.error('copyTo failed:', err)
reject(err)
}
)
},
reject
)
},
reject
)
}).catch(reject)
// #endif
// #ifndef APP-PLUS
// 非 APP 端降级使用 uni.saveFile
uni.saveFile({
tempFilePath: tempFilePath,
success: (saveRes) => {
const record = {
id: Date.now().toString(),
name: fileName,
path: saveRes.savedFilePath,
size: fileSize || 0,
time: Date.now()
}
const list = getDownloadList()
list.unshift(record)
uni.setStorageSync(STORAGE_KEY, list)
resolve(record)
},
fail: reject
})
// #endif
})
}
/**
* 删除单条下载记录及其文件
* @param {string} id 记录 ID
*/
export function deleteDownload(id) {
return new Promise((resolve, reject) => {
const list = getDownloadList()
const record = list.find(item => item.id === id)
if (!record) {
reject(new Error('记录不存在'))
return
}
// #ifdef APP-PLUS
// 删除公共目录下的文件
plus.io.resolveLocalFileSystemURL(
record.path,
(fileEntry) => {
fileEntry.remove(
() => {
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
},
() => {
// 文件不存在也清除记录
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
}
)
},
() => {
// 无法解析路径,直接清除记录
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
}
)
// #endif
// #ifndef APP-PLUS
uni.removeSavedFile({
filePath: record.path,
success: () => {
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
},
fail: () => {
const newList = list.filter(item => item.id !== id)
uni.setStorageSync(STORAGE_KEY, newList)
resolve()
}
})
// #endif
})
}
/**
* 清空所有下载记录和文件
*/
export function clearAllDownloads() {
return new Promise((resolve) => {
const list = getDownloadList()
if (list.length === 0) {
resolve()
return
}
// #ifdef APP-PLUS
let count = 0
const done = () => {
count++
if (count >= list.length) {
uni.setStorageSync(STORAGE_KEY, [])
resolve()
}
}
list.forEach(record => {
plus.io.resolveLocalFileSystemURL(
record.path,
(fileEntry) => {
fileEntry.remove(done, done)
},
done
)
})
setTimeout(() => {
uni.setStorageSync(STORAGE_KEY, [])
resolve()
}, 5000)
// #endif
// #ifndef APP-PLUS
let count = 0
list.forEach(record => {
uni.removeSavedFile({
filePath: record.path,
success: () => { count++; if (count >= list.length) { uni.setStorageSync(STORAGE_KEY, []); resolve() } },
fail: () => { count++; if (count >= list.length) { uni.setStorageSync(STORAGE_KEY, []); resolve() } }
})
})
if (list.length === 0) resolve()
setTimeout(() => { uni.setStorageSync(STORAGE_KEY, []); resolve() }, 3000)
// #endif
})
}
/**
* 格式化文件大小为可读字符串
* @param {number} bytes 字节数
* @returns {string}
*/
export function formatFileSize(bytes) {
if (!bytes || bytes <= 0) return '未知大小'
const units = ['B', 'KB', 'MB', 'GB']
let i = 0
let size = bytes
while (size >= 1024 && i < units.length - 1) {
size /= 1024
i++
}
return size.toFixed(i === 0 ? 0 : 1) + ' ' + units[i]
}
/**
* 格式化时间戳为可读日期
* @param {number} timestamp
* @returns {string}
*/
export function formatTime(timestamp) {
const d = new Date(timestamp)
const pad = n => String(n).padStart(2, '0')
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}