Files
015/front/components/Result/ImageCompressResult.vue

194 lines
5.3 KiB
Vue

<script setup lang="ts">
import { useQuery } from "@tanstack/vue-query";
import { AsyncButton, Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { filesize } from "filesize";
import useMyAppShare from "~/composables/useMyAppShare";
import { toast } from "vue-sonner";
const emit = defineEmits<{
(e: "change", key: string): void;
}>();
const props = defineProps<{
data: { file: File; config: any; handle_type: string; file_id: string };
}>();
const { data } = useQuery({
queryKey: ["create-image-compress", props?.data?.file_id],
queryFn: async () => {
const { file_id } = props?.data || {};
const data = await $fetch<{
code: number;
data: {
id?: string;
};
}>(`/api/image/compress`, {
method: "POST",
body: {
file_id,
},
});
return data?.data;
},
staleTime: Infinity,
});
const taskId = computed(() => data?.value?.id);
const { data: taskData, refetch } = useQuery({
queryKey: ["image-compress-task", taskId],
queryFn: async () => {
const data = await $fetch<{
code: number;
data: {
result: {
old_file: {
id: string;
size: number;
};
new_file: {
id: string;
size: number;
};
}[];
status: "success" | "retry" | "archived";
err?: {
message?: string;
retry?: number;
max_retry?: number;
};
};
}>(`/api/image/compress/${taskId.value}`);
return data?.data;
},
enabled: !!taskId.value,
});
const { downloadFile, createFileShare } = useMyAppShare();
const { counter, pause } = useInterval(2000, { controls: true });
watch(
() => counter.value,
() => {
if (["success", "archived"].includes(taskData.value?.status ?? "")) {
pause();
return;
}
refetch();
},
);
watch(
() => taskData.value?.err?.retry,
(newVal, oldVal) => {
if (!oldVal || !newVal || !taskData.value?.err?.max_retry) {
return;
}
if (newVal <= taskData.value?.err?.max_retry) {
toast.error(`处理错误: ${taskData.value?.err?.message}, 将再次重试`);
}
},
);
</script>
<template>
<div class="flex flex-col gap-3">
<h2 class="text-lg">上传成功</h2>
<div class="flex flex-col gap-1 items-center">
<div class="flex flex-col h-30 items-center justify-center">
<FilePreviewView :value="props?.data?.file" />
</div>
</div>
<div
v-if="taskData?.status === 'success'"
class="flex flex-col gap-2"
v-for="item in taskData?.result"
>
<div
class="bg-white/80 p-2 rounded-md w-full flex flex-row items-center justify-between gap-2"
>
<div class="flex flex-row gap-2 items-center max-w-2/3">
<div
class="flex flex-row items-center justify-center rounded-md bg-black/5 p-2"
>
<LucideImage />
</div>
<div class="truncate w-auto">{{ props?.data?.file?.name }}</div>
<div class="flex flex-row gap-2 items-center text-sm shrink-0">
<span class="opacity-75">{{
filesize(item.new_file.size ?? 0)
}}</span>
<span
class="bg-green-200 text-green-600 rounded-md px-1 py-0.5 flex flex-row gap-1 items-center text-xs"
>
<LucideChevronDown class="size-4" />
{{
((1 - item.new_file.size / item.old_file.size) * 100).toFixed(
2,
)
}}%
</span>
</div>
</div>
<AsyncButton
variant="outline"
class="bg-black/5"
size="icon"
@click="
async () => {
const data = await createFileShare({
file_id: item.new_file.id,
config: {
download_nums: 1,
expire_time: 60,
has_pickup_code: false,
has_password: false,
},
file_name: props?.data?.file?.name,
});
const { id } = data?.data || {};
if (!id) {
return;
}
await downloadFile(id);
}
"
>
<LucideDownload />
</AsyncButton>
</div>
</div>
<div
v-else-if="taskData?.status !== 'retry' && !!taskData?.err?.message"
class="flex flex-col gap-2"
>
<div
class="w-full h-16 flex flex-row items-center gap-3 bg-white/80 rounded-md p-2"
>
<div
class="size-10 flex items-center justify-center rounded-md bg-red-200"
>
<LucideAlertTriangle class="size-5 text-red-600" />
</div>
<div class="text-sm">
{{
`经过 ${taskData?.err?.retry} 次重试后任务处理失败: ${taskData?.err?.message}`
}}
</div>
</div>
<div class="flex flex-row justify-center">
<Button @click="emit('change', 'input')"> 返回首页 </Button>
</div>
</div>
<div
v-else="taskData?.status !== 'retry' && !!taskData?.err?.message"
class="flex flex-col gap-2"
>
<Skeleton
class="w-full h-16 flex flex-row items-center justify-between"
v-for="i in 3"
/>
</div>
</div>
</template>