fix
This commit is contained in:
@@ -55,36 +55,42 @@ watch(search, (value) => {
|
||||
<van-button size="small" type="primary" plain @click="selectCategory(0)">Все</van-button>
|
||||
</div>
|
||||
|
||||
<van-search v-model="search" placeholder="Поиск по категории" />
|
||||
<div class="entity-popup-body">
|
||||
<van-search v-model="search" placeholder="Поиск по категории" />
|
||||
|
||||
<van-loading v-if="loading" class="entity-state" type="spinner">Загрузка...</van-loading>
|
||||
<van-loading v-if="loading" class="entity-state" type="spinner">Загрузка...</van-loading>
|
||||
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:title="category.name"
|
||||
:label="category.name_group || `ID: ${category.id}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectCategory(category.id)"
|
||||
>
|
||||
<template #right-icon>
|
||||
<van-icon v-if="selectedCategoryId === category.id" name="success" color="#1989fa" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:title="category.name"
|
||||
:label="category.name_group || `ID: ${category.id}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectCategory(category.id)"
|
||||
>
|
||||
<template #right-icon>
|
||||
<van-icon v-if="selectedCategoryId === category.id" name="success" color="#1989fa" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<van-empty v-if="categories.length === 0" description="Категории не найдены" />
|
||||
</template>
|
||||
<van-empty v-if="categories.length === 0" description="Категории не найдены" />
|
||||
</template>
|
||||
</div>
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.entity-popup {
|
||||
min-height: 55vh;
|
||||
padding: 18px 0 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 70vh;
|
||||
max-height: 70vh;
|
||||
overflow: hidden;
|
||||
padding: 18px 0 16px;
|
||||
}
|
||||
|
||||
.entity-popup-header {
|
||||
@@ -106,6 +112,12 @@ watch(search, (value) => {
|
||||
padding: 36px 0;
|
||||
}
|
||||
|
||||
.entity-popup-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.entity-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -58,40 +58,46 @@ watch(search, (value) => {
|
||||
<van-button size="small" type="primary" plain @click="selectCounterparty(0)">Все</van-button>
|
||||
</div>
|
||||
|
||||
<van-search v-model="search" placeholder="Поиск по имени" />
|
||||
<div class="counterparty-popup-body">
|
||||
<van-search v-model="search" placeholder="Поиск по имени" />
|
||||
|
||||
<van-loading v-if="loading" class="counterparty-state" type="spinner">Загрузка...</van-loading>
|
||||
<van-loading v-if="loading" class="counterparty-state" type="spinner">Загрузка...</van-loading>
|
||||
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="counterparty in counterparties"
|
||||
:key="counterparty.id"
|
||||
:title="counterparty.name"
|
||||
:label="`ID: ${counterparty.id}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectCounterparty(counterparty.id)"
|
||||
>
|
||||
<template #right-icon>
|
||||
<van-icon
|
||||
v-if="selectedCounterpartyId === counterparty.id"
|
||||
name="success"
|
||||
color="#1989fa"
|
||||
/>
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="counterparty in counterparties"
|
||||
:key="counterparty.id"
|
||||
:title="counterparty.name"
|
||||
:label="`ID: ${counterparty.id}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectCounterparty(counterparty.id)"
|
||||
>
|
||||
<template #right-icon>
|
||||
<van-icon
|
||||
v-if="selectedCounterpartyId === counterparty.id"
|
||||
name="success"
|
||||
color="#1989fa"
|
||||
/>
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<van-empty v-if="counterparties.length === 0" description="Контрагенты не найдены" />
|
||||
</template>
|
||||
<van-empty v-if="counterparties.length === 0" description="Контрагенты не найдены" />
|
||||
</template>
|
||||
</div>
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.counterparty-popup {
|
||||
min-height: 55vh;
|
||||
padding: 18px 0 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 70vh;
|
||||
max-height: 70vh;
|
||||
overflow: hidden;
|
||||
padding: 18px 0 16px;
|
||||
}
|
||||
|
||||
.counterparty-popup-header {
|
||||
@@ -113,6 +119,12 @@ watch(search, (value) => {
|
||||
padding: 36px 0;
|
||||
}
|
||||
|
||||
.counterparty-popup-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.counterparty-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -53,36 +53,42 @@ watch(search, (value) => {
|
||||
<van-button size="small" type="primary" plain @click="selectFrc(0)">Все</van-button>
|
||||
</div>
|
||||
|
||||
<van-search v-model="search" placeholder="Поиск по названию" />
|
||||
<div class="entity-popup-body">
|
||||
<van-search v-model="search" placeholder="Поиск по названию" />
|
||||
|
||||
<van-loading v-if="loading" class="entity-state" type="spinner">Загрузка...</van-loading>
|
||||
<van-loading v-if="loading" class="entity-state" type="spinner">Загрузка...</van-loading>
|
||||
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="frc in frcs"
|
||||
:key="frc.id"
|
||||
:title="frc.name"
|
||||
:label="`Баланс: ${frc.balance}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectFrc(frc.id)"
|
||||
>
|
||||
<template #right-icon>
|
||||
<van-icon v-if="selectedFrcId === frc.id" name="success" color="#1989fa" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="frc in frcs"
|
||||
:key="frc.id"
|
||||
:title="frc.name"
|
||||
:label="`Баланс: ${frc.balance}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectFrc(frc.id)"
|
||||
>
|
||||
<template #right-icon>
|
||||
<van-icon v-if="selectedFrcId === frc.id" name="success" color="#1989fa" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<van-empty v-if="frcs.length === 0" description="ФРЦ не найдены" />
|
||||
</template>
|
||||
<van-empty v-if="frcs.length === 0" description="ФРЦ не найдены" />
|
||||
</template>
|
||||
</div>
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.entity-popup {
|
||||
min-height: 55vh;
|
||||
padding: 18px 0 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 70vh;
|
||||
max-height: 70vh;
|
||||
overflow: hidden;
|
||||
padding: 18px 0 16px;
|
||||
}
|
||||
|
||||
.entity-popup-header {
|
||||
@@ -104,6 +110,12 @@ watch(search, (value) => {
|
||||
padding: 36px 0;
|
||||
}
|
||||
|
||||
.entity-popup-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.entity-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -7,10 +7,9 @@ import {
|
||||
type PDFDocumentProxy,
|
||||
type RenderTask,
|
||||
} from "pdfjs-dist";
|
||||
import { computed, nextTick, onBeforeUnmount, ref, watch } from "vue";
|
||||
import { computed, nextTick, onBeforeUnmount, ref, shallowRef, watch } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
localPath: string;
|
||||
scanUrl: string;
|
||||
title: string;
|
||||
}>();
|
||||
@@ -23,7 +22,7 @@ GlobalWorkerOptions.workerSrc = new URL(
|
||||
).toString();
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
||||
const pdfDocument = ref<PDFDocumentProxy | null>(null);
|
||||
const pdfDocument = shallowRef<PDFDocumentProxy | null>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref("");
|
||||
const pageNumber = ref(1);
|
||||
@@ -41,19 +40,17 @@ async function loadDocument() {
|
||||
|
||||
loading.value = true;
|
||||
error.value = "";
|
||||
let task: PDFDocumentLoadingTask | null = null;
|
||||
|
||||
try {
|
||||
const bytes = await invoke<number[]>("load_application_file", {
|
||||
localPath: props.localPath,
|
||||
scanUrl: props.scanUrl,
|
||||
const bytes = await invoke<number[]>("load_remote_file", {
|
||||
url: props.scanUrl,
|
||||
});
|
||||
|
||||
if (!bytes.length) {
|
||||
throw new Error("Файл пустой");
|
||||
}
|
||||
|
||||
task = getDocument({ data: new Uint8Array(bytes) });
|
||||
const task = getDocument({ data: new Uint8Array(bytes) });
|
||||
loadingTask = task;
|
||||
|
||||
const document = await task.promise;
|
||||
@@ -66,12 +63,13 @@ async function loadDocument() {
|
||||
pageCount.value = document.numPages;
|
||||
pageNumber.value = 1;
|
||||
scale.value = 1.1;
|
||||
loading.value = false;
|
||||
await nextTick();
|
||||
await renderPage();
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : "Не удалось открыть PDF";
|
||||
} finally {
|
||||
loading.value = false;
|
||||
if (task && loadingTask === task) {
|
||||
if (loadingTask) {
|
||||
loadingTask = null;
|
||||
}
|
||||
}
|
||||
@@ -156,7 +154,7 @@ async function zoomOut() {
|
||||
await renderPage();
|
||||
}
|
||||
|
||||
watch([show, () => props.localPath, () => props.scanUrl], async ([visible]) => {
|
||||
watch([show, () => props.scanUrl], async ([visible]) => {
|
||||
if (visible) {
|
||||
await nextTick();
|
||||
await loadDocument();
|
||||
|
||||
@@ -57,36 +57,42 @@ watch(search, (value) => {
|
||||
<van-button size="small" type="primary" plain @click="selectProject(0)">Все</van-button>
|
||||
</div>
|
||||
|
||||
<van-search v-model="search" placeholder="Поиск по проекту" />
|
||||
<div class="entity-popup-body">
|
||||
<van-search v-model="search" placeholder="Поиск по проекту" />
|
||||
|
||||
<van-loading v-if="loading" class="entity-state" type="spinner">Загрузка...</van-loading>
|
||||
<van-loading v-if="loading" class="entity-state" type="spinner">Загрузка...</van-loading>
|
||||
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="project in projects"
|
||||
:key="project.id"
|
||||
:title="project.short_name || project.name"
|
||||
:label="project.full_name || `ID: ${project.id}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectProject(project.id)"
|
||||
>
|
||||
<template #right-icon>
|
||||
<van-icon v-if="selectedProjectId === project.id" name="success" color="#1989fa" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="project in projects"
|
||||
:key="project.id"
|
||||
:title="project.short_name || project.name"
|
||||
:label="project.full_name || `ID: ${project.id}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectProject(project.id)"
|
||||
>
|
||||
<template #right-icon>
|
||||
<van-icon v-if="selectedProjectId === project.id" name="success" color="#1989fa" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<van-empty v-if="projects.length === 0" description="Проекты не найдены" />
|
||||
</template>
|
||||
<van-empty v-if="projects.length === 0" description="Проекты не найдены" />
|
||||
</template>
|
||||
</div>
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.entity-popup {
|
||||
min-height: 55vh;
|
||||
padding: 18px 0 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 70vh;
|
||||
max-height: 70vh;
|
||||
overflow: hidden;
|
||||
padding: 18px 0 16px;
|
||||
}
|
||||
|
||||
.entity-popup-header {
|
||||
@@ -108,6 +114,12 @@ watch(search, (value) => {
|
||||
padding: 36px 0;
|
||||
}
|
||||
|
||||
.entity-popup-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.entity-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { openPath, openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { openUrl } from "@tauri-apps/plugin-opener";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { showToast } from "vant";
|
||||
@@ -173,14 +174,16 @@ function formatMoney(value: unknown) {
|
||||
}).format(amount);
|
||||
}
|
||||
|
||||
async function openApplicationFile(localPath: string) {
|
||||
if (!localPath) {
|
||||
async function openApplicationFile(scanUrl: string) {
|
||||
if (!scanUrl) {
|
||||
showToast("Файл не скачан");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await openPath(localPath);
|
||||
await invoke("open_remote_file", {
|
||||
url: scanUrl,
|
||||
});
|
||||
} catch (err) {
|
||||
showToast(errorMessage(err, "Не удалось открыть файл"));
|
||||
}
|
||||
@@ -209,6 +212,10 @@ function openPdfPreview(file: ContractApplicationFile) {
|
||||
}
|
||||
|
||||
function errorMessage(err: unknown, fallback: string) {
|
||||
if (typeof err === "object" && err && "detail" in err) {
|
||||
return String((err as { detail: unknown }).detail);
|
||||
}
|
||||
|
||||
if (err instanceof Error) {
|
||||
return err.message;
|
||||
}
|
||||
@@ -435,7 +442,7 @@ onMounted(() => {
|
||||
size="small"
|
||||
type="primary"
|
||||
plain
|
||||
:disabled="!file.local_path"
|
||||
:disabled="!file.scan_url"
|
||||
@click="openPdfPreview(file)"
|
||||
>
|
||||
Просмотр
|
||||
@@ -443,8 +450,8 @@ onMounted(() => {
|
||||
<van-button
|
||||
size="small"
|
||||
plain
|
||||
:disabled="!file.local_path"
|
||||
@click="openApplicationFile(file.local_path)"
|
||||
:disabled="!file.scan_url"
|
||||
@click="openApplicationFile(file.scan_url)"
|
||||
>
|
||||
Открыть
|
||||
</van-button>
|
||||
@@ -507,7 +514,6 @@ onMounted(() => {
|
||||
|
||||
<PdfPreview
|
||||
v-model:show="pdfPreviewVisible"
|
||||
:local-path="pdfPreviewFile?.local_path ?? ''"
|
||||
:scan-url="pdfPreviewFile?.scan_url ?? ''"
|
||||
:title="pdfPreviewTitle"
|
||||
/>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { employeeApi } from "../../../generated/api";
|
||||
import type { EmployeeListParams } from "../../../generated/models";
|
||||
import RemoteImage from "../../../shared/components/RemoteImage.vue";
|
||||
import { useModelApi } from "../../../shared/composables/useModelApi";
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
@@ -58,39 +59,45 @@ watch(search, (value) => {
|
||||
<van-button size="small" type="primary" plain @click="selectEmployee(0)">Не указан</van-button>
|
||||
</div>
|
||||
|
||||
<van-search v-model="search" placeholder="Поиск по имени" />
|
||||
<div class="employee-popup-body">
|
||||
<van-search v-model="search" placeholder="Поиск по имени" />
|
||||
|
||||
<van-loading v-if="loading" class="employee-state" type="spinner">Загрузка...</van-loading>
|
||||
<van-loading v-if="loading" class="employee-state" type="spinner">Загрузка...</van-loading>
|
||||
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="employee in employees"
|
||||
:key="employee.id"
|
||||
:title="employee.name"
|
||||
:label="`ID: ${employee.id}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectEmployee(employee.id)"
|
||||
>
|
||||
<template #icon>
|
||||
<van-image class="employee-avatar" round width="36" height="36" :src="employee.avatar_small ?? ''" />
|
||||
</template>
|
||||
<template #right-icon>
|
||||
<van-icon v-if="selectedEmployeeId === employee.id" name="success" color="#1989fa" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<template v-else>
|
||||
<van-cell-group inset>
|
||||
<van-cell
|
||||
v-for="employee in employees"
|
||||
:key="employee.id"
|
||||
:title="employee.name"
|
||||
:label="`ID: ${employee.id}`"
|
||||
clickable
|
||||
center
|
||||
@click="selectEmployee(employee.id)"
|
||||
>
|
||||
<template #icon>
|
||||
<RemoteImage class="employee-avatar" round width="36" height="36" :src="employee.avatar_small" />
|
||||
</template>
|
||||
<template #right-icon>
|
||||
<van-icon v-if="selectedEmployeeId === employee.id" name="success" color="#1989fa" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<van-empty v-if="employees.length === 0" description="Сотрудники не найдены" />
|
||||
</template>
|
||||
<van-empty v-if="employees.length === 0" description="Сотрудники не найдены" />
|
||||
</template>
|
||||
</div>
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.employee-popup {
|
||||
min-height: 55vh;
|
||||
padding: 18px 0 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 70vh;
|
||||
max-height: 70vh;
|
||||
overflow: hidden;
|
||||
padding: 18px 0 16px;
|
||||
}
|
||||
|
||||
.employee-popup-header {
|
||||
@@ -116,6 +123,12 @@ watch(search, (value) => {
|
||||
padding: 36px 0;
|
||||
}
|
||||
|
||||
.employee-popup-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.employee-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { Task } from "../../../generated/models";
|
||||
import RemoteImage from "../../../shared/components/RemoteImage.vue";
|
||||
|
||||
type TaskItem = Task;
|
||||
|
||||
@@ -25,7 +26,7 @@ const emit = defineEmits<{
|
||||
@click="emit('open', `/tasks/${props.item.id}`)"
|
||||
>
|
||||
<template #icon>
|
||||
<van-image
|
||||
<RemoteImage
|
||||
class="task-avatar"
|
||||
round
|
||||
width="36"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { computed, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { taskApi } from "../../../generated/api";
|
||||
import RemoteImage from "../../../shared/components/RemoteImage.vue";
|
||||
import { useModelApi } from "../../../shared/composables/useModelApi";
|
||||
|
||||
const route = useRoute();
|
||||
@@ -93,7 +94,7 @@ onMounted(() => {
|
||||
|
||||
<div v-else class="message-list">
|
||||
<article v-for="message in task.message_set" :key="message.id" class="message-item">
|
||||
<van-image class="message-avatar" round width="40" height="40" :src="message.author.avatar_small ?? ''" />
|
||||
<RemoteImage class="message-avatar" round width="40" height="40" :src="message.author.avatar_small" />
|
||||
<div class="message-bubble">
|
||||
<div class="message-author">{{ personName(message.author) }}</div>
|
||||
<div class="message-text">{{ message.text }}</div>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { useRemoteFileUrl } from "../composables/useRemoteFileUrl";
|
||||
|
||||
const props = defineProps<{
|
||||
src?: string | null;
|
||||
}>();
|
||||
|
||||
const sourceUrl = computed(() => props.src);
|
||||
const { objectUrl } = useRemoteFileUrl(sourceUrl);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<van-image v-bind="$attrs" :src="objectUrl" />
|
||||
</template>
|
||||
@@ -0,0 +1,57 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { onBeforeUnmount, ref, watch, type Ref } from "vue";
|
||||
|
||||
export function useRemoteFileUrl(sourceUrl: Ref<string | null | undefined>) {
|
||||
const objectUrl = ref("");
|
||||
const loading = ref(false);
|
||||
const error = ref("");
|
||||
let loadId = 0;
|
||||
|
||||
async function load(url: string | null | undefined) {
|
||||
const currentLoadId = ++loadId;
|
||||
revokeObjectUrl();
|
||||
error.value = "";
|
||||
|
||||
if (!url) {
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const bytes = await invoke<number[]>("load_remote_file", { url });
|
||||
if (currentLoadId !== loadId) {
|
||||
return;
|
||||
}
|
||||
|
||||
objectUrl.value = URL.createObjectURL(new Blob([new Uint8Array(bytes)]));
|
||||
} catch (err) {
|
||||
if (currentLoadId !== loadId) {
|
||||
return;
|
||||
}
|
||||
|
||||
error.value = err instanceof Error ? err.message : "Не удалось загрузить файл";
|
||||
} finally {
|
||||
if (currentLoadId === loadId) {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function revokeObjectUrl() {
|
||||
if (objectUrl.value) {
|
||||
URL.revokeObjectURL(objectUrl.value);
|
||||
objectUrl.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
watch(sourceUrl, (url) => void load(url), { immediate: true });
|
||||
onBeforeUnmount(revokeObjectUrl);
|
||||
|
||||
return {
|
||||
objectUrl,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user