p2p-explorer-web/src/pages/file/item/fileTranserView.vue
2026-05-07 22:02:25 +08:00

221 lines
4.7 KiB
Vue

<template>
<div class="transfer-container">
<div class="transfer-header">
传输列表 ({{ tasks.length }})
<div class="transfer-header-btns">
<Button type="ghost" class="clear-btn" @click="clearTasks">清理</Button>
<Button
type="ghost"
class="toggle-btn"
@click="isExpanded = !isExpanded"
>{{ isExpanded ? "▼" : "▲" }}</Button
>
</div>
</div>
<Transition name="file-transfer-view">
<div v-show="isExpanded" class="transfer-content">
<div
v-for="task in tasks"
:key="task.fileData.path"
class="transfer-item"
>
<div class="task-header">
<div class="file-name">{{ task.fileData.name }}</div>
<div class="task-status" :class="task.progress.status">
{{ getStatusText(task.progress.status) }}
</div>
</div>
<div class="file-info">
<span>{{ formatSize(task.fileData.size) }}</span>
<span v-if="task.progress.speed"
>· {{ formatSize(task.progress.speed) }}/s</span
>
</div>
<Progress
:percent="task.progress.percent"
:status="getProgressStatus(task.progress.status)"
/>
</div>
</div>
</Transition>
</div>
</template>
<script setup lang="ts">
import { Button } from "ant-design-vue";
import { Progress } from "ant-design-vue";
import {
fileTransferMgrInstance,
type TransferTask,
TransferStatus,
} from "../utils/fileTransfer";
import { type Ref, ref } from "vue";
import { formatSize } from "../utils/common";
const isExpanded = ref(true);
const tasks: Ref<TransferTask[]> = ref([]);
const clearTasks = () => {
fileTransferMgrInstance.getAllFileTransfers().forEach((transfer) => {
transfer.clearCompletedTasks();
});
updateTasks();
};
const getStatusText = (status: TransferStatus) => {
const statusMap: Record<TransferStatus, string> = {
[TransferStatus.WAITING]: "等待中",
[TransferStatus.SENDING]: "发送中",
[TransferStatus.RECEIVING]: "接收中",
[TransferStatus.COMPLETED]: "已完成",
[TransferStatus.ERROR]: "失败",
[TransferStatus.PAUSED]: "已暂停",
};
return statusMap[status] || status;
};
const getProgressStatus = (status: TransferStatus) => {
if (status === TransferStatus.ERROR) return "exception";
if (status === TransferStatus.COMPLETED) return "success";
return "active";
};
const updateTasks = () => {
tasks.value = Array.from(
fileTransferMgrInstance
.getAllFileTransfers()
.flatMap((transfer) => transfer.getTasks()),
);
// newTasks.forEach((task) => {
// const index = tasks.value.findIndex(
// (t) => t.fileData.path === task.fileData.path
// );
// if (index === -1) {
// tasks.value.push(task);
// }
// });
};
fileTransferMgrInstance.onTransferChanged((transfer) => {
updateTasks();
});
updateTasks();
</script>
<style scoped>
.transfer-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
z-index: 10;
}
.transfer-header {
padding: 10px 20px;
background: #f5f5f5;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid #ddd;
font-weight: 500;
}
.transfer-content {
overflow-y: auto;
overflow-x: hidden;
max-height: 600px;
}
.transfer-item {
padding: 12px;
padding-left: 20px;
padding-right: 20px;
border-bottom: 1px solid #eee;
}
.task-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.file-name {
font-weight: 500;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-status {
font-size: 12px;
padding: 2px 8px;
border-radius: 10px;
background: #f0f0f0;
}
.task-status.completed {
background: #e6f7e6;
color: #52c41a;
}
.task-status.error {
background: #fff1f0;
color: #ff4d4f;
}
.task-status.sending,
.task-status.receiving {
background: #e6f7ff;
color: #1890ff;
}
.task-status.waiting {
background: #f5f5f5;
color: #8c8c8c;
}
.task-status.paused {
background: #fff7e6;
color: #faad14;
}
.file-info {
margin-bottom: 8px;
font-size: 12px;
color: #666;
}
.toggle-icon {
font-size: 12px;
}
.transfer-header-btns {
display: flex;
align-items: flex-end;
}
.clear-btn {
margin-left: 10px;
}
/* 过渡动画 */
.file-transfer-view-enter-active,
.file-transfer-view-leave-active {
transition: max-height 0.5s ease-in-out;
overflow: hidden;
}
.file-transfer-view-enter-from,
.file-transfer-view-leave-to {
max-height: 0;
}
.file-transfer-view-enter-to,
.file-transfer-view-leave-from {
max-height: 600px;
}
</style>