This commit is contained in:
che
2026-07-27 10:51:18 +05:00
parent 560b3fe44e
commit c1b24206b9
13 changed files with 405 additions and 226 deletions
@@ -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;
+34 -22
View File
@@ -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;
+9 -11
View File
@@ -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();
+34 -22
View File
@@ -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"
/>