海信医疗-远程超声管理平台-信创国产化
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.

369 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 : 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.is_success
)
"
size="mini"
>
{{
getStatusText(
scope.row.transfer_type,
scope.row.is_success
)
}}
</el-tag>
</template>
</el-table-column>
<!-- 操作按钮 -->
<el-table-column label="操作" width="180" 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
v-if="scope.row.transfer_type === 1"
type="text"
size="mini"
icon="el-icon-upload2"
disabled
>
已上传
</el-button>
<el-button
type="text"
size="mini"
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";
},
// 获取状态文字,与 Qt UpdateTransferStatus 逻辑一致
// type: 1=upload, 3=download, 9=other
getStatusText(type, isSuccess) {
// 与 Qt 的 switch(type << isSuccess) 对应
const key = type << (isSuccess ? 1 : 0);
const statusMap = {
1: "上传中", // upload=1, is_success=false -> 1 << 0 = 1 (新插入时显示上传中)
2: "上传成功", // upload=1, is_success=true -> 1 << 1 = 2
3: "下载中", // download=3, is_success=false -> 3 << 0 = 3 (新插入时显示下载中)
6: "下载成功", // download=3, is_success=true -> 3 << 1 = 6
9: "其他", // other=9
18: "其他完成", // other=9, is_success=true -> 9 << 1 = 18
};
// 实际场景:已有的记录失败时
if (!isSuccess && key === 1) return "上传失败";
if (!isSuccess && key === 3) return "下载失败";
return statusMap[key] || "未知";
},
// 获取状态标签类型(Element UI 样式)
getStatusType(type, isSuccess) {
if (!isSuccess) return "danger";
if (type === 1) return "success";
if (type === 3) return "success";
return "info";
},
// 下载文件
handleDownload(item) {
if (item.file_url) {
window.open(item.file_url, "_blank");
} else {
this.$message.warning("文件链接不可用");
}
},
// 打开文件位置
handleOpenLocation(item) {
if (item.local_path) {
this.$modal.msg("浏览器安全限制:请下载后在下载列表打开文件夹");
const a = document.createElement("a");
a.href = item.file_url;
a.download = item.file_name;
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;
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>