mirror of
https://github.com/keven1024/015.git
synced 2026-05-26 15:13:30 +00:00
194 lines
5.3 KiB
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>
|