barneo-file-service
Version:
Комплексная библиотека Vue 3 для работы с файлами в приложениях Barneo. Предоставляет мощную функциональность для загрузки, управления и обработки файлов с валидацией, отслеживанием прогресса и поддержкой localStorage.
675 lines (561 loc) • 21.5 kB
Markdown
# Barneo File Service
Комплексная библиотека Vue 3 для работы с файлами в приложениях Barneo. Предоставляет мощную функциональность для загрузки, управления и обработки файлов с валидацией, отслеживанием прогресса и поддержкой localStorage.
> **Версия:** 1.1.1
> **Дата релиза:** 2 сентября 2025
> **Лицензия:** MIT
## 🚀 Основные возможности
- **Загрузка файлов**: Поддержка drag & drop, множественной загрузки
- **Валидация**: Проверка размера, типа и количества файлов
- **Отслеживание прогресса**: Детальная информация о процессе загрузки
- **Управление состоянием**: Реактивное состояние файлов и ошибок
- **Хранилище**: Сохранение файлов в localStorage для повторного использования
- **Обработка ошибок**: Возможность повтора неудачных загрузок
- **Кастомизация**: Гибкая настройка через props, slots и события
## 📦 Установка
```bash
npm install barneo-file-service
```
## 🔧 Быстрый старт
### 1. Подключение плагина
```typescript
import { createApp } from "vue";
import BarneoFileService from "barneo-file-service";
import { AppName } from "barneo-file-service";
import "barneo-file-service/style";
const app = createApp(App);
app.use(BarneoFileService, {
apiUrl: "https://api.example.com/upload",
appName: AppName.PIM,
maxFiles: 10,
maxFileSize: 50 * 1024 * 1024, // 50MB
allowedTypes: ["image/jpeg", "image/png", "application/pdf"],
onUploadSuccess: (results) => console.log("Файлы загружены:", results),
});
app.mount("#app");
```
### 2. Базовое использование компонента
```vue
<template>
<FileUploader
title="Загрузить документы"
@upload-success="handleUploadSuccess"
@upload-error="handleUploadError"
/>
</template>
<script setup>
const handleUploadSuccess = (files) => {
console.log("Загружено файлов:", files.length);
};
const handleUploadError = (error) => {
console.error("Ошибка загрузки:", error);
};
</script>
```
## 🎯 Сценарии использования
### 1. Загрузка документов с валидацией
```vue
<template>
<FileUploader
title="Загрузить документы"
:max-files="5"
:max-file-size="10 * 1024 * 1024"
:allowed-types="['application/pdf', 'application/msword']"
@upload-success="handleDocumentsUpload"
/>
</template>
<script setup>
const handleDocumentsUpload = (files) => {
// Обработка загруженных документов
files.forEach((file) => {
console.log(`Документ загружен: ${file.originalName}`);
});
};
</script>
```
### 2. Загрузка изображений с предпросмотром
```vue
<template>
<FileUploader
button-type="input"
title="Загрузить изображения"
:allowed-types="['image/jpeg', 'image/png', 'image/webp']"
:multiple="true"
@upload-success="handleImagesUpload"
/>
</template>
<script setup>
const handleImagesUpload = (files) => {
// Создание предпросмотра изображений
files.forEach((file) => {
const img = new Image();
img.src = file.url;
// Добавление в галерею
});
};
</script>
```
### 3. Использование с кастомным UI
```vue
<template>
<FileUploader
button-type="default"
button-text="Выбрать файлы"
button-variant="primary"
@upload-success="handleUpload"
>
<template #button="{ openModal, buttonText }">
<button @click="openModal" class="custom-button">
{{ buttonText }}
</button>
</template>
<template #modal-header="{ title, closeModal }">
<div class="custom-header">
<h2>{{ title }}</h2>
<button @click="closeModal">✕</button>
</div>
</template>
</FileUploader>
</template>
```
### 4. Работа с сохраненными файлами
```vue
<template>
<FileUploader title="Управление файлами" @upload-success="handleUpload" />
</template>
<script setup>
import { useFileService } from "barneo-file-service";
const fileService = useFileService();
const handleUpload = (files) => {
// Файлы автоматически сохраняются в localStorage
console.log("Файлы сохранены в хранилище");
// Получение сохраненных файлов
const storedFiles = fileService.storedFiles.value;
console.log("Сохраненные файлы:", storedFiles);
};
</script>
```
### 5. Управление загруженными файлами через props
```vue
<template>
<FileUploader
button-type="input"
:uploaded-files="myUploadedFiles"
@files-cleared="handleFilesCleared"
@upload-success="handleUploadSuccess"
/>
</template>
<script setup>
import { ref } from "vue";
const myUploadedFiles = ref([
{
id: 1,
originalName: "document.pdf",
name: "document.pdf",
size: 1024000,
type: "application/pdf",
url: "https://example.com/document.pdf",
},
]);
const handleFilesCleared = () => {
// Файлы были очищены пользователем
console.log("Файлы очищены");
// Здесь можно обновить состояние в родительском компоненте
myUploadedFiles.value = [];
};
const handleUploadSuccess = (files) => {
// Добавляем новые загруженные файлы к существующим
myUploadedFiles.value.push(...files);
};
</script>
```
### 6. Вызов методов компонента из родительского компонента
```vue
<template>
<div>
<button @click="clearFiles">Очистить файлы</button>
<button @click="openModal">Открыть модальное окно</button>
<button @click="closeModal">Закрыть модальное окно</button>
<FileUploader
ref="fileUploaderRef"
button-type="input"
:uploaded-files="myUploadedFiles"
@files-cleared="handleFilesCleared"
/>
</div>
</template>
<script setup>
import { ref } from "vue";
const fileUploaderRef = ref();
const myUploadedFiles = ref([
{
id: 1,
originalName: "document.pdf",
name: "document.pdf",
size: 1024000,
type: "application/pdf",
url: "https://example.com/document.pdf",
},
]);
const clearFiles = () => {
// Вызываем метод clearUploadedFiles из дочернего компонента
fileUploaderRef.value?.clearUploadedFiles();
};
const openModal = () => {
// Открываем модальное окно программно
fileUploaderRef.value?.openModal();
};
const closeModal = () => {
// Закрываем модальное окно программно
fileUploaderRef.value?.closeModal();
};
const handleFilesCleared = () => {
console.log("Файлы очищены");
myUploadedFiles.value = [];
};
</script>
```
## 📋 API Reference
### FileUploader Component
#### Props
| Prop | Type | Default | Description |
| --------------------- | -------------------------------------------------- | ------------------- | -------------------------------------------------------------- |
| `title` | `string` | `'Загрузка файлов'` | Заголовок модального окна |
| `buttonText` | `string` | `'Загрузить файлы'` | Текст кнопки |
| `buttonSize` | `'small' \| 'medium' \| 'large'` | `'medium'` | Размер кнопки |
| `buttonVariant` | `'primary' \| 'secondary' \| 'outline' \| 'ghost'` | `'primary'` | Стиль кнопки |
| `buttonType` | `'default' \| 'input'` | `'default'` | Тип отображения кнопки |
| `buttonIcon` | `string` | `undefined` | CSS класс иконки |
| `autoCloseOnSuccess` | `boolean` | `true` | Автоматически закрывать модальное окно после успешной загрузки |
| `showCancelButton` | `boolean` | `true` | Показывать ли кнопку отмены |
| `cancelButtonText` | `string` | `'Отмена'` | Текст кнопки отмены |
| `modalMaxWidth` | `string` | `'600px'` | Максимальная ширина модального окна |
| `modalPosition` | `'center' \| 'top' \| 'bottom'` | `'center'` | Позиция модального окна |
| `closeOnOverlayClick` | `boolean` | `true` | Закрывать ли модальное окно при клике вне его |
| `showAnimations` | `boolean` | `true` | Показывать ли анимации |
| `uploadedFiles` | `FileUploadResponse[]` | `undefined` | Список загруженных файлов для отображения |
#### Events
| Event | Payload | Description |
| ----------------- | ---------------------------------- | ------------------------------------------- |
| `upload-success` | `FileUploadResponse[]` | Файлы успешно загружены |
| `upload-complete` | `[FileUploadResponse[], string[]]` | Загрузка завершена (успешно или с ошибками) |
| `upload-error` | `Error` | Ошибка загрузки |
| `files-selected` | `File[]` | Файлы выбраны (но еще не загружены) |
| `modal-open` | `void` | Модальное окно открыто |
| `modal-close` | `void` | Модальное окно закрыто |
| `files-cleared` | `void` | Загруженные файлы очищены |
#### Exposed Methods
Методы, которые можно вызывать из родительского компонента через ref:
| Method | Parameters | Return Type | Description |
| ----------------------- | ---------- | ----------- | ---------------------------- |
| `clearUploadedFiles` | - | `void` | Очищает загруженные файлы |
| `openModal` | - | `void` | Открывает модальное окно |
| `closeModal` | - | `void` | Закрывает модальное окно |
| `uploadSelectedFiles` | - | `Promise` | Загружает выбранные файлы |
| `clearFiles` | - | `void` | Очищает выбранные файлы |
| `clearErrors` | - | `void` | Очищает ошибки валидации |
| `clearCompletedUploads` | - | `void` | Очищает завершенные загрузки |
| `retryFailedUploads` | - | `void` | Повторяет неудачные загрузки |
#### Slots
##### `button` - Слот для кнопки (по умолчанию)
```vue
<template
#button="{ openModal, buttonText, buttonSize, buttonVariant, buttonIcon }"
>
<button @click="openModal" class="custom-button">
{{ buttonText }}
</button>
</template>
```
##### `button-input` - Слот для input типа кнопки
```vue
<template
#button-input="{
openModal,
title,
uploadedFiles,
clearUploadedFiles,
formatFileSize,
allowedTypes,
}"
>
<div class="custom-input">
<span>{{ title }}</span>
<button @click="openModal">Выбрать</button>
<button @click="clearUploadedFiles">Очистить</button>
</div>
</template>
```
##### `modal-header` - Слот для заголовка модального окна
```vue
<template #modal-header="{ title, closeModal }">
<div class="custom-header">
<h2>{{ title }}</h2>
<button @click="closeModal">Закрыть</button>
</div>
</template>
```
##### `file-drop-zone` - Слот для области выбора файлов
```vue
<template
#file-drop-zone="{
selectFiles,
isDragOver,
selectedFiles,
errors,
triggerFileInput,
formatFileSize,
config,
}"
>
<div
@drop="selectFiles"
@dragover.prevent
:class="{ 'drag-over': isDragOver }"
>
<p>Перетащите файлы сюда</p>
<button @click="triggerFileInput">Выбрать файлы</button>
</div>
</template>
```
##### `file-list` - Слот для списка выбранных файлов
```vue
<template
#file-list="{
selectedFiles,
removeFile,
clearFiles,
isFileUploading,
formatFileSize,
isUploading,
}"
>
<div class="file-list">
<div v-for="(file, index) in selectedFiles" :key="index">
<span>{{ file.name }}</span>
<span>{{ formatFileSize(file.size) }}</span>
<button @click="removeFile(index)" :disabled="isFileUploading(file)">
Удалить
</button>
</div>
</div>
</template>
```
##### `stored-files` - Слот для списка сохраненных файлов
```vue
<template
#stored-files="{
storedFiles,
selectedStoredFiles,
toggleStoredFileSelection,
isStoredFileSelected,
removeStoredFile,
formatFileSize,
formatDate,
hasStoredFiles,
}"
>
<div v-if="hasStoredFiles" class="stored-files">
<div v-for="file in storedFiles" :key="file.id">
<input
type="checkbox"
:checked="isStoredFileSelected(file.id)"
@change="toggleStoredFileSelection(file.id)"
/>
<span>{{ file.originalName }}</span>
<span>{{ formatFileSize(file.size) }}</span>
<span>{{ formatDate(file.uploadedAt) }}</span>
<button @click="removeStoredFile(file.id)">Удалить</button>
</div>
</div>
</template>
```
##### `action-buttons` - Слот для кнопок действий
```vue
<template
#action-buttons="{
uploadFiles,
selectStoredFiles,
clearFiles,
retryFailedUploads,
isUploading,
hasSelectedFiles,
hasErrors,
activeMode,
hasSelectedStoredFiles,
closeModal,
showCancelButton,
cancelButtonText,
}"
>
<div class="action-buttons">
<button v-if="showCancelButton" @click="closeModal">
{{ cancelButtonText }}
</button>
<button
v-if="activeMode === 'upload'"
@click="uploadFiles"
:disabled="!hasSelectedFiles || isUploading"
>
{{ isUploading ? "Загрузка..." : "Загрузить" }}
</button>
<button
v-else-if="activeMode === 'select'"
@click="selectStoredFiles"
:disabled="!hasSelectedStoredFiles"
>
Выбрать
</button>
</div>
</template>
```
### Composables
#### `useFileService()`
Основной composable для работы с файлами.
```typescript
import { useFileService } from "barneo-file-service";
const fileService = useFileService();
// Состояние
const selectedFiles = fileService.selectedFiles;
const errors = fileService.errors;
const isUploading = fileService.isUploading;
const totalProgress = fileService.totalProgress;
// Методы
const results = await fileService.uploadSelectedFiles();
fileService.selectFiles(files);
fileService.clearFiles();
fileService.clearErrors();
```
#### `useFileInput(options)`
Composable для управления выбором файлов.
```typescript
import { useFileInput } from "barneo-file-service";
const {
selectedFiles,
errors,
selectFiles,
removeFile,
clearFiles,
isFileSelected,
totalSize,
} = useFileInput({
maxFiles: 5,
maxFileSize: 10 * 1024 * 1024, // 10MB
allowedTypes: ["image/jpeg", "image/png"],
multiple: true,
});
```
#### `useFileUploader(options)`
Composable для управления загрузкой файлов.
```typescript
import { useFileUploader } from "barneo-file-service";
const {
uploads,
isUploading,
totalProgress,
uploadFile,
uploadFiles,
retryUpload,
clearUploads,
} = useFileUploader({
apiUrl: "https://api.example.com/upload",
appName: AppName.PIM,
onProgress: (progress) => console.log(`Progress: ${progress}%`),
onError: (error) => console.error("Upload failed:", error),
});
```
#### `useFileStorage()`
Composable для управления хранилищем файлов.
```typescript
import { useFileStorage } from "barneo-file-service";
const {
storedFiles,
selectedStoredFiles,
addStoredFile,
removeStoredFile,
clearStoredFiles,
addSelectedStoredFile,
clearSelectedStoredFiles,
} = useFileStorage();
```
### Сервисы
#### `FileUploadService`
Сервис для загрузки файлов на сервер.
```typescript
import { FileUploadService } from "barneo-file-service";
const service = new FileUploadService({
apiUrl: "https://api.example.com/upload",
appName: AppName.PIM,
headers: { Authorization: "Bearer token" },
timeout: 30000,
});
// Загрузка одного файла
const result = await service.uploadFile(file, {
appName: AppName.PIM,
onProgress: (progress) => console.log(`Progress: ${progress}%`),
onError: (error) => console.error("Upload failed:", error),
});
// Загрузка нескольких файлов
const results = await service.uploadFiles([file1, file2], {
appName: AppName.PIM,
});
```
## 🎨 Кастомизация
### CSS переменные
Библиотека использует CSS переменные для кастомизации стилей:
```css
:root {
--primary-color: #0091ea;
--primary-hover: #0077cc;
--gray-50: #fafafa;
--gray-100: #f9fafb;
--gray-200: #f3f4f6;
--gray-300: #e5e7eb;
--gray-400: #d1d5db;
--gray-500: #9ca3af;
--gray-600: #6b7280;
--gray-700: #374151;
--gray-900: #111827;
--error-color: #ef4444;
--success-color: #10b981;
--border-radius-sm: 6px;
--border-radius-md: 8px;
--border-radius-lg: 10px;
--border-radius-xl: 12px;
--transition: all 0.15s ease;
--shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
--shadow-lg: 0 25px 50px -12px rgba(0, 0, 0, 0.15);
}
```
### Кастомные стили
```vue
<template>
<FileUploader class="custom-uploader" />
</template>
<style>
.custom-uploader {
--primary-color: #ff6b6b;
--border-radius-md: 12px;
}
.custom-uploader .file-uploader__button {
background: linear-gradient(45deg, #ff6b6b, #ee5a24);
border: none;
color: white;
font-weight: bold;
}
</style>
```
## 🔧 Разработка
```bash
# Установка зависимостей
npm install
# Разработка
npm run dev
# Сборка
npm run build
# Проверка типов
npm run type-check
# Линтинг
npm run lint
```
## 📄 Лицензия
MIT