You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
364 lines
12 KiB
364 lines
12 KiB
<template> |
|
<div class="app-container"> |
|
<!-- 传输管理器卡片 --> |
|
<el-card> |
|
<!-- 头部:标题 + 清空按钮 --> |
|
<div slot="header"> |
|
<span>传输管理器</span> |
|
<el-button |
|
style="float: right; padding: 3px 0" |
|
type="text" |
|
icon="el-icon-delete" |
|
:disabled="groupedRecords.length === 0" |
|
@click="handleClearAll" |
|
> |
|
清空记录 |
|
</el-button> |
|
</div> |
|
<div v-loading="loading" class="container"> |
|
<template v-if="groupedRecords.length > 0"> |
|
<div v-for="(group, index) in groupedRecords" :key="index"> |
|
<!-- 日期分割线 --> |
|
<el-divider> |
|
<el-tag type="info"> |
|
{{ group.date }} ({{ group.items.length }}) |
|
</el-tag> |
|
</el-divider> |
|
<!-- 传输记录表格 --> |
|
<el-table :data="group.items" :show-header="false"> |
|
<el-table-column label="缩略图标" width="80" align="center"> |
|
<template slot-scope="scope"> |
|
<div class="file-thumb"> |
|
<i :class="getFileIconClass(scope.row.file_name)" /> |
|
</div> |
|
</template> |
|
</el-table-column> |
|
<el-table-column |
|
prop="file_name" |
|
label="文件名" |
|
show-overflow-tooltip |
|
/> |
|
<el-table-column |
|
label="文件大小" |
|
width="100" |
|
align="center" |
|
show-overflow-tooltip |
|
> |
|
<template slot-scope="scope"> |
|
{{ formatFileSize(scope.row.file_size) }} |
|
</template> |
|
</el-table-column> |
|
<el-table-column label="进度" width="150" align="center"> |
|
<template slot-scope="scope"> |
|
<div class="progress-container"> |
|
<el-progress |
|
:percentage=" |
|
scope.row.is_success |
|
? 100 |
|
: scope.row.upload_percent || 0 |
|
" |
|
/> |
|
</div> |
|
</template> |
|
</el-table-column> |
|
<el-table-column |
|
label="上传时间" |
|
width="80" |
|
align="center" |
|
show-overflow-tooltip |
|
> |
|
<template slot-scope="scope"> |
|
{{ formatTime(scope.row.time) }} |
|
</template> |
|
</el-table-column> |
|
<el-table-column label="状态" width="100" align="center"> |
|
<template slot-scope="scope"> |
|
<el-tag |
|
:type="getStatusType(scope.row.transfer_type, scope.row)" |
|
size="mini" |
|
> |
|
{{ getStatusText(scope.row.transfer_type, scope.row) }} |
|
</el-tag> |
|
</template> |
|
</el-table-column> |
|
<!-- 操作按钮 --> |
|
<el-table-column label="操作" width="220" align="center"> |
|
<template slot-scope="scope"> |
|
<el-button |
|
v-if="scope.row.transfer_type === 3 && scope.row.file_url" |
|
type="text" |
|
icon="el-icon-view" |
|
@click="handleDownload(scope.row)" |
|
> |
|
打开 |
|
</el-button> |
|
<el-button |
|
v-if="scope.row.transfer_type === 3 && scope.row.file_url" |
|
type="text" |
|
icon="el-icon-folder-opened" |
|
@click="handleOpenLocation(scope.row)" |
|
> |
|
打开位置 |
|
</el-button> |
|
<el-button |
|
type="text" |
|
icon="el-icon-delete" |
|
style="color: #f56c6c" |
|
@click="handleDeleteItem(scope.row)" |
|
> |
|
删除 |
|
</el-button> |
|
</template> |
|
</el-table-column> |
|
</el-table> |
|
</div> |
|
</template> |
|
<el-empty v-else description="暂无文件传输记录" /> |
|
</div> |
|
</el-card> |
|
</div> |
|
</template> |
|
<script> |
|
const STORAGE_KEY = "file_transfer_records"; |
|
export default { |
|
name: "UtilityPage", |
|
data() { |
|
return { |
|
loading: false, |
|
transferRecords: [], |
|
}; |
|
}, |
|
computed: { |
|
// 按日期分组,与 Qt DisplayFiletransferinfo 逻辑一致 |
|
groupedRecords() { |
|
const groups = {}; |
|
for (const item of this.transferRecords) { |
|
const date = this.formatDate(item.time); |
|
if (!groups[date]) { |
|
groups[date] = { date, items: [] }; |
|
} |
|
groups[date].items.push(item); |
|
} |
|
// 按日期降序,同一天内按时间降序 |
|
return Object.values(groups).sort((a, b) => { |
|
return new Date(b.date) - new Date(a.date); |
|
}); |
|
}, |
|
}, |
|
mounted() { |
|
this.loadTransferRecords(); |
|
}, |
|
methods: { |
|
// 从 localStorage 加载文件传输记录(替代 Qt 的 SQLite 查询) |
|
loadTransferRecords() { |
|
this.loading = true; |
|
try { |
|
const data = localStorage.getItem(STORAGE_KEY); |
|
if (data) { |
|
this.transferRecords = JSON.parse(data); |
|
} |
|
} catch (e) { |
|
console.error("加载文件传输记录失败", e); |
|
this.transferRecords = []; |
|
} finally { |
|
this.loading = false; |
|
} |
|
}, |
|
// 保存到 localStorage |
|
saveTransferRecords() { |
|
try { |
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(this.transferRecords)); |
|
} catch (e) { |
|
console.error("保存文件传输记录失败", e); |
|
} |
|
}, |
|
// 格式化日期,与 Qt "yyyy-MM-dd" 一致 |
|
formatDate(timeStr) { |
|
if (!timeStr) return ""; |
|
const date = new Date(timeStr); |
|
const y = date.getFullYear(); |
|
const m = String(date.getMonth() + 1).padStart(2, "0"); |
|
const d = String(date.getDate()).padStart(2, "0"); |
|
return `${y}-${m}-${d}`; |
|
}, |
|
|
|
// 格式化时间,与 Qt "hh:mm" 一致 |
|
formatTime(timeStr) { |
|
if (!timeStr) return ""; |
|
const date = new Date(timeStr); |
|
const h = String(date.getHours()).padStart(2, "0"); |
|
const m = String(date.getMinutes()).padStart(2, "0"); |
|
return `${h}:${m}`; |
|
}, |
|
// 格式化文件大小,与 Qt getSendFileSize 一致 |
|
formatFileSize(size) { |
|
if (!size || size < 0) return "0.00B"; |
|
if (size >= 1024 * 1024) { |
|
return (size / (1024.0 * 1024.0)).toFixed(2) + "M"; |
|
} else if (size >= 1024) { |
|
return (size / 1024.0).toFixed(2) + "K"; |
|
} |
|
return size.toFixed(2) + "B"; |
|
}, |
|
// 获取文件图标类名,与 Qt getFileIconPath 逻辑一致 |
|
getFileIconClass(fileName) { |
|
if (!fileName) return "el-icon-document"; |
|
const suffix = fileName.split(".").pop().toLowerCase(); |
|
const iconMap = { |
|
txt: "el-icon-tickets", |
|
pdf: "el-icon-document-copy", |
|
mp3: "el-icon-headset", |
|
wav: "el-icon-headset", |
|
avi: "el-icon-video-camera", |
|
mp4: "el-icon-video-camera", |
|
wmv: "el-icon-video-camera", |
|
mov: "el-icon-video-camera", |
|
zip: "el-icon-folder-opened", |
|
rar: "el-icon-folder-opened", |
|
ppt: "el-icon-data-board", |
|
pps: "el-icon-data-board", |
|
xls: "el-icon-s-grid", |
|
xlsx: "el-icon-s-grid", |
|
doc: "el-icon-document", |
|
docx: "el-icon-document", |
|
jpg: "el-icon-picture", |
|
jpeg: "el-icon-picture", |
|
jpe: "el-icon-picture", |
|
bmp: "el-icon-picture", |
|
gif: "el-icon-picture", |
|
png: "el-icon-picture", |
|
tif: "el-icon-picture", |
|
tiff: "el-icon-picture", |
|
}; |
|
return iconMap[suffix] || "el-icon-document"; |
|
}, |
|
|
|
// 获取状态文字 |
|
getStatusText(type, row) { |
|
const isSuccess = row.is_success; |
|
const isUploading = row.is_uploading; |
|
if (type === 3) { |
|
// 知识库上传类型 |
|
if (isUploading && !isSuccess) return "上传中"; |
|
if (!isSuccess) return "上传失败"; |
|
return "上传成功"; |
|
} |
|
// 其他类型 |
|
if (!isSuccess) return "失败"; |
|
return "成功"; |
|
}, |
|
|
|
// 获取状态标签类型(Element UI 样式) |
|
getStatusType(type, row) { |
|
const isSuccess = row.is_success; |
|
const isUploading = row.is_uploading; |
|
if (isUploading && !isSuccess) return "warning"; |
|
if (!isSuccess) return "danger"; |
|
return "success"; |
|
}, |
|
|
|
// 下载文件 |
|
handleDownload(item) { |
|
if (item.file_url) { |
|
window.open(item.file_url, "_blank"); |
|
} else { |
|
this.$message.warning("文件链接不可用"); |
|
} |
|
}, |
|
// 打开文件位置 |
|
handleOpenLocation(item) { |
|
if (item.file_url) { |
|
this.$modal.msg("浏览器安全限制:请下载文件后在下载列表中打开文件夹"); |
|
// 先触发下载 |
|
const a = document.createElement("a"); |
|
a.href = item.file_url; |
|
a.download = item.file_name; |
|
a.target = "_blank"; |
|
document.body.appendChild(a); |
|
a.click(); |
|
document.body.removeChild(a); |
|
} else { |
|
this.$message.warning("文件路径不可用"); |
|
} |
|
}, |
|
// 删除单条记录 |
|
handleDeleteItem(item) { |
|
this.$confirm("确认删除该传输记录?", "提示", { type: "warning" }) |
|
.then(() => { |
|
this.transferRecords = this.transferRecords.filter( |
|
(r) => r.id !== item.id |
|
); |
|
this.saveTransferRecords(); |
|
this.$message.success("删除成功"); |
|
}) |
|
.catch(() => {}); |
|
}, |
|
// 清空所有记录,与 Qt on_clearButton_clicked 逻辑一致 |
|
handleClearAll() { |
|
this.$modal |
|
.confirm("是否要清除本地传输记录?") |
|
.then(() => { |
|
this.transferRecords = []; |
|
this.saveTransferRecords(); |
|
this.$message.success("记录已清空"); |
|
}) |
|
.catch(() => {}); |
|
}, |
|
// 添加一条新的传输记录(供外部调用,与 Qt UpdateToolsWidgetSlot 对应) |
|
addTransferRecord(record) { |
|
const newRecord = { |
|
id: record.id || Date.now(), |
|
user_id: record.user_id || 0, |
|
file_name: record.file_name || "", |
|
local_path: record.local_path || "", |
|
file_size: record.file_size || 0, |
|
transfer_type: record.transfer_type || 9, |
|
is_success: record.is_success || false, |
|
time: record.time || new Date().toISOString(), |
|
file_url: record.file_url || "", |
|
}; |
|
// 插入到列表顶部(与 Qt InsertToolsItemWidget 对应) |
|
this.transferRecords.unshift(newRecord); |
|
this.saveTransferRecords(); |
|
}, |
|
// 更新传输状态(供外部调用,与 Qt UpdateFinishedToolsWidgetSlot 对应) |
|
updateTransferStatus(id, isSuccess) { |
|
const record = this.transferRecords.find((r) => r.id === id); |
|
if (record) { |
|
record.is_success = isSuccess; |
|
record.is_uploading = false; |
|
this.saveTransferRecords(); |
|
} |
|
}, |
|
// 更新上传进度(供外部调用) |
|
updateUploadProgress(id, percent) { |
|
const record = this.transferRecords.find((r) => r.id === id); |
|
if (record) { |
|
record.upload_percent = percent; |
|
record.is_uploading = percent < 100; |
|
this.saveTransferRecords(); |
|
} |
|
}, |
|
}, |
|
}; |
|
</script> |
|
|
|
|
|
<style lang="scss" scoped> |
|
.container { |
|
height: calc(100vh - 230px); |
|
overflow-y: auto; |
|
.file-thumb { |
|
width: 42px; |
|
height: 42px; |
|
border-radius: 6px; |
|
background: #655dd4; |
|
color: #fff; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
font-size: 20px; |
|
margin: 0 auto; |
|
} |
|
} |
|
</style> |