feat(fetch):add download progress bar
parent
d667d97f62
commit
63ad80b0bc
|
@ -19,3 +19,18 @@ export async function fetchUrlFile(
|
||||||
console.log("on create download task: ", taskID);
|
console.log("on create download task: ", taskID);
|
||||||
return taskID;
|
return taskID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DownloadTask = {
|
||||||
|
filename: string;
|
||||||
|
pathname: string;
|
||||||
|
progress: number;
|
||||||
|
savedSize: number;
|
||||||
|
taskID: string;
|
||||||
|
totalSize: number;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function queryDownloadTask(taskID: string): Promise<DownloadTask> {
|
||||||
|
const res = await fetchURL(`/api/download/${taskID}`, {});
|
||||||
|
return await res.json();
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
@keyup.enter="submit"
|
@keyup.enter="submit"
|
||||||
v-model.trim="fetchUrl"
|
v-model.trim="fetchUrl"
|
||||||
tabindex="1"
|
tabindex="1"
|
||||||
|
:disabled="isDownloading"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
@keyup.enter="submit"
|
@keyup.enter="submit"
|
||||||
v-model.trim="saveName"
|
v-model.trim="saveName"
|
||||||
tabindex="2"
|
tabindex="2"
|
||||||
|
:disabled="isDownloading"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -33,7 +35,7 @@
|
||||||
@click="layoutStore.closeHovers"
|
@click="layoutStore.closeHovers"
|
||||||
:aria-label="t('buttons.cancel')"
|
:aria-label="t('buttons.cancel')"
|
||||||
:title="t('buttons.cancel')"
|
:title="t('buttons.cancel')"
|
||||||
tabindex="3"
|
tabindex="4"
|
||||||
>
|
>
|
||||||
{{ t("buttons.cancel") }}
|
{{ t("buttons.cancel") }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -42,11 +44,22 @@
|
||||||
:aria-label="t('buttons.create')"
|
:aria-label="t('buttons.create')"
|
||||||
:title="t('buttons.create')"
|
:title="t('buttons.create')"
|
||||||
@click="submit"
|
@click="submit"
|
||||||
tabindex="2"
|
tabindex="3"
|
||||||
|
:disabled="isDownloading"
|
||||||
>
|
>
|
||||||
{{ t("buttons.create") }}
|
{{ t("buttons.create") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="progress > 0" class="material-progress-container">
|
||||||
|
<div
|
||||||
|
class="material-progress-bar"
|
||||||
|
id="downloadProgress"
|
||||||
|
:style="{ width: Math.floor(progress * 100) + '%' }"
|
||||||
|
></div>
|
||||||
|
<div class="material-progress-label">
|
||||||
|
{{ (progress * 100).toFixed(1) + "%" }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -54,7 +67,7 @@
|
||||||
import { useLayoutStore } from "@/stores/layout.ts";
|
import { useLayoutStore } from "@/stores/layout.ts";
|
||||||
import { useFileStore } from "@/stores/file.ts";
|
import { useFileStore } from "@/stores/file.ts";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { inject, ref, watch } from "vue";
|
import { computed, inject, onMounted, ref, watch } from "vue";
|
||||||
import url from "@/utils/url.ts";
|
import url from "@/utils/url.ts";
|
||||||
import { fetcher as api } from "@/api";
|
import { fetcher as api } from "@/api";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
@ -70,6 +83,11 @@ const { t } = useI18n();
|
||||||
const fetchUrl = ref<string>("");
|
const fetchUrl = ref<string>("");
|
||||||
const saveName = ref<string>("");
|
const saveName = ref<string>("");
|
||||||
const taskID = ref<string>("");
|
const taskID = ref<string>("");
|
||||||
|
const progress = ref<number>(0);
|
||||||
|
|
||||||
|
const isDownloading = computed(() => {
|
||||||
|
return taskID.value !== "" && progress.value < 1 && progress.value > 0;
|
||||||
|
});
|
||||||
|
|
||||||
watch(fetchUrl, (value) => {
|
watch(fetchUrl, (value) => {
|
||||||
try {
|
try {
|
||||||
|
@ -113,8 +131,20 @@ const submit = async (event: Event) => {
|
||||||
$showError(e);
|
$showError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
layoutStore.closeHovers();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setInterval(async () => {
|
||||||
|
if (!taskID.value) return;
|
||||||
|
const task = await api.queryDownloadTask(taskID.value);
|
||||||
|
if (!task) return;
|
||||||
|
console.log("fetch task info", task);
|
||||||
|
progress.value = task.progress;
|
||||||
|
if (task.progress >= 1) {
|
||||||
|
taskID.value = "";
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|
|
@ -503,3 +503,32 @@ html[dir="rtl"] .credits {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.material-progress-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #2196F3; /* Material蓝色 */
|
||||||
|
width: 0%;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-progress-label {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
Loading…
Reference in New Issue