barneo-file-service
Version:
Комплексная библиотека Vue 3 для работы с файлами в приложениях Barneo. Предоставляет мощную функциональность для загрузки, управления и обработки файлов с валидацией, отслеживанием прогресса и поддержкой localStorage.
212 lines (186 loc) • 6.41 kB
text/typescript
import { ref, computed } from "vue";
import type { FileInputOptions, FileInputState } from "../types";
/**
* Composable для управления выбором файлов
*
* Предоставляет функциональность для выбора, валидации и управления файлами
* с поддержкой drag & drop, валидации размера и типа файлов
*
* @param options - Опции для настройки валидации и ограничений
* @returns Объект с состоянием и методами для работы с файлами
*
* @example
* ```typescript
* const {
* selectedFiles,
* errors,
* selectFiles,
* removeFile,
* clearFiles,
* isFileSelected,
* totalSize
* } = useFileInput({
* maxFiles: 5,
* maxFileSize: 10 * 1024 * 1024, // 10MB
* allowedTypes: ['image/jpeg', 'image/png'],
* multiple: true
* })
* ```
*/
export function useFileInput(options: FileInputOptions = {}) {
const {
maxFiles = 10,
maxFileSize = 50 * 1024 * 1024, // 50MB по умолчанию
allowedTypes = [],
multiple = true,
} = options;
// Состояние
/** Массив выбранных файлов */
const selectedFiles = ref<File[]>([]);
/** Массив ошибок валидации */
const errors = ref<string[]>([]);
// Computed свойства
/** Есть ли выбранные файлы */
const isFileSelected = computed(() => selectedFiles.value.length > 0);
/** Общий размер всех выбранных файлов в байтах */
const totalSize = computed(() =>
selectedFiles.value.reduce((total, file) => total + file.size, 0)
);
/** Массив уникальных MIME-типов выбранных файлов */
const fileTypes = computed(() => [
...new Set(selectedFiles.value.map((file) => file.type)),
]);
/** Можно ли выбрать еще файлы */
const canSelectMore = computed(
() => multiple && selectedFiles.value.length < maxFiles
);
/**
* Выбирает файлы с валидацией
*
* @param files - FileList или массив файлов для выбора
*/
const selectFiles = (files: FileList | File[]) => {
const fileArray = Array.from(files);
const newErrors: string[] = [];
// Проверяем каждый файл
fileArray.forEach((file) => {
// Проверка количества файлов
if (selectedFiles.value.length >= maxFiles) {
newErrors.push(`Максимальное количество файлов: ${maxFiles}`);
return;
}
// Проверка размера файла
if (file.size > maxFileSize) {
newErrors.push(
`Файл ${file.name} превышает максимальный размер ${formatFileSize(
maxFileSize
)}`
);
return;
}
// Проверка типа файла
if (allowedTypes.length > 0 && !allowedTypes.includes(file.type)) {
newErrors.push(`Тип файла ${file.name} не поддерживается`);
return;
}
// Добавляем файл если нет ошибок
selectedFiles.value.push(file);
});
// Обновляем ошибки
errors.value = [...errors.value, ...newErrors];
};
/**
* Удаляет файл по индексу
*
* @param index - Индекс файла для удаления
*/
const removeFile = (index: number) => {
if (index >= 0 && index < selectedFiles.value.length) {
selectedFiles.value.splice(index, 1);
}
};
/**
* Удаляет файл по имени
*
* @param fileName - Имя файла для удаления
*/
const removeFileByName = (fileName: string) => {
const index = selectedFiles.value.findIndex(
(file) => file.name === fileName
);
if (index !== -1) {
removeFile(index);
}
};
/**
* Очищает все выбранные файлы и ошибки
*/
const clearFiles = () => {
selectedFiles.value = [];
errors.value = [];
};
/**
* Заменяет файл на новый с валидацией
*
* @param oldFile - Старый файл для замены
* @param newFile - Новый файл
* @returns true если замена прошла успешно, false если есть ошибки валидации
*/
const replaceFile = (oldFile: File, newFile: File) => {
const index = selectedFiles.value.findIndex((file) => file === oldFile);
if (index !== -1) {
// Проверяем новый файл
if (newFile.size > maxFileSize) {
errors.value.push(
`Файл ${newFile.name} превышает максимальный размер ${formatFileSize(
maxFileSize
)}`
);
return false;
}
if (allowedTypes.length > 0 && !allowedTypes.includes(newFile.type)) {
errors.value.push(`Тип файла ${newFile.name} не поддерживается`);
return false;
}
selectedFiles.value[index] = newFile;
return true;
}
return false;
};
/**
* Очищает все ошибки валидации
*/
const clearErrors = () => {
errors.value = [];
};
// Вспомогательные функции
const formatFileSize = (bytes: number): string => {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
};
return {
// Состояние
selectedFiles,
errors,
// Computed
isFileSelected,
totalSize,
fileTypes,
canSelectMore,
// Методы
selectFiles,
removeFile,
removeFileByName,
clearFiles,
replaceFile,
clearErrors,
// Константы
maxFiles,
maxFileSize,
allowedTypes,
multiple,
};
}