|
|
|
|
@ -48,7 +48,7 @@
|
|
|
|
|
link
|
|
|
|
|
type="primary"
|
|
|
|
|
class="file-item"
|
|
|
|
|
@click="viewReport(f.filePath)"
|
|
|
|
|
@click="viewReport(f.filePath, f.fileName)"
|
|
|
|
|
>
|
|
|
|
|
<el-icon><Document /></el-icon>{{ f.fileName }}
|
|
|
|
|
</el-button>
|
|
|
|
|
@ -59,13 +59,13 @@
|
|
|
|
|
<div class="section-title">{{ $t('orderDetail.statusChange') }}</div>
|
|
|
|
|
<el-timeline>
|
|
|
|
|
<el-timeline-item
|
|
|
|
|
v-for="(log, i) in detail.logs || []"
|
|
|
|
|
v-for="(log, i) in detail.statusLogs || []"
|
|
|
|
|
:key="i"
|
|
|
|
|
:timestamp="log.createdAt"
|
|
|
|
|
placement="top"
|
|
|
|
|
>
|
|
|
|
|
<div class="log-title">{{ log.action }} · {{ log.operator }}</div>
|
|
|
|
|
<div class="log-remark">{{ log.remark }}</div>
|
|
|
|
|
<div class="log-title">{{ log.toStatus }} · {{ log.operator }}</div>
|
|
|
|
|
<div class="log-remark" v-html="log.remark"></div>
|
|
|
|
|
</el-timeline-item>
|
|
|
|
|
</el-timeline>
|
|
|
|
|
</div>
|
|
|
|
|
@ -80,10 +80,15 @@
|
|
|
|
|
:before-close="closePreview"
|
|
|
|
|
>
|
|
|
|
|
<iframe
|
|
|
|
|
v-if="previewUrl"
|
|
|
|
|
v-if="previewType === 'pdf' && previewUrl"
|
|
|
|
|
:src="previewUrl"
|
|
|
|
|
class="preview-iframe"
|
|
|
|
|
/>
|
|
|
|
|
<img
|
|
|
|
|
v-else-if="previewType === 'image' && previewUrl"
|
|
|
|
|
:src="previewUrl"
|
|
|
|
|
class="preview-image"
|
|
|
|
|
/>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
@ -118,12 +123,14 @@ const goProcess = () => router.push(`/admin/orders/process/${route.params.id}`);
|
|
|
|
|
|
|
|
|
|
const previewVisible = ref(false);
|
|
|
|
|
const previewUrl = ref("");
|
|
|
|
|
const previewType = ref(""); // 'pdf' | 'image' | ''
|
|
|
|
|
|
|
|
|
|
const cleanupPreviewUrl = () => {
|
|
|
|
|
if (previewUrl.value) {
|
|
|
|
|
URL.revokeObjectURL(previewUrl.value);
|
|
|
|
|
previewUrl.value = "";
|
|
|
|
|
}
|
|
|
|
|
previewType.value = "";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const closePreview = () => {
|
|
|
|
|
@ -131,14 +138,49 @@ const closePreview = () => {
|
|
|
|
|
cleanupPreviewUrl();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const viewReport = async (attachmentPath) => {
|
|
|
|
|
// 根据文件名/扩展名+blob 的 mime 判断文件类型
|
|
|
|
|
const getFileType = (fileName, blob) => {
|
|
|
|
|
const name = String(fileName || "");
|
|
|
|
|
const ext = name.split(".").pop()?.toLowerCase() || "";
|
|
|
|
|
const mime = blob?.type || "";
|
|
|
|
|
if (ext === "pdf" || mime === "application/pdf") return "pdf";
|
|
|
|
|
if (
|
|
|
|
|
["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"].includes(ext) ||
|
|
|
|
|
(mime && mime.startsWith("image/"))
|
|
|
|
|
) {
|
|
|
|
|
return "image";
|
|
|
|
|
}
|
|
|
|
|
return "other";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 浏览器直接下载 blob
|
|
|
|
|
const downloadBlob = (blob, fileName) => {
|
|
|
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
|
const a = document.createElement("a");
|
|
|
|
|
a.href = url;
|
|
|
|
|
a.download = fileName || "download";
|
|
|
|
|
document.body.appendChild(a);
|
|
|
|
|
a.click();
|
|
|
|
|
document.body.removeChild(a);
|
|
|
|
|
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const viewReport = async (attachmentPath, originalName) => {
|
|
|
|
|
if (!attachmentPath) return;
|
|
|
|
|
const fileName = String(attachmentPath);
|
|
|
|
|
const path = String(attachmentPath);
|
|
|
|
|
// 优先用原始文件名作为下载名,没有则回退到路径
|
|
|
|
|
const downloadName = originalName || path;
|
|
|
|
|
try {
|
|
|
|
|
const blob = await getUploadFile(fileName);
|
|
|
|
|
cleanupPreviewUrl();
|
|
|
|
|
previewUrl.value = URL.createObjectURL(blob);
|
|
|
|
|
previewVisible.value = true;
|
|
|
|
|
const blob = await getUploadFile(path);
|
|
|
|
|
const type = getFileType(downloadName, blob);
|
|
|
|
|
if (type === "pdf" || type === "image") {
|
|
|
|
|
cleanupPreviewUrl();
|
|
|
|
|
previewUrl.value = URL.createObjectURL(blob);
|
|
|
|
|
previewType.value = type;
|
|
|
|
|
previewVisible.value = true;
|
|
|
|
|
} else {
|
|
|
|
|
downloadBlob(blob, downloadName);
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
ElMessage.error(e?.message || $t('msg.failed'));
|
|
|
|
|
}
|
|
|
|
|
@ -205,4 +247,11 @@ onMounted(loadDetail);
|
|
|
|
|
height: 80vh;
|
|
|
|
|
border: 0;
|
|
|
|
|
}
|
|
|
|
|
.preview-image {
|
|
|
|
|
display: block;
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
max-height: 80vh;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|