changes
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import slugify from "slugify";
|
||||
import process from "@inertiajs/inertia-vue3/.eslintrc.js";
|
||||
|
||||
export const helpers = {
|
||||
methods: {
|
||||
@@ -58,16 +57,283 @@ export const helpers = {
|
||||
|
||||
GET_BASE_URL() {
|
||||
if (typeof window === 'undefined') {
|
||||
return process.env.APP_URL || '/'; // Используем env-переменную на сервере
|
||||
return import.meta.env.VITE_APP_URL || '/';
|
||||
}
|
||||
return window.location.origin + '/';
|
||||
},
|
||||
|
||||
GET_BASE_STORAGE_URL() {
|
||||
if (typeof window === 'undefined') {
|
||||
return process.env.APP_URL || '/storage/'; // Используем env-переменную на сервере
|
||||
return (import.meta.env.VITE_APP_URL || '/') + '/storage/';
|
||||
}
|
||||
return window.location.origin + '/storage/';
|
||||
},
|
||||
|
||||
SET_DOCUMENT_TITLE(title, subtitle = 'Dashboard') {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
if (title) {
|
||||
document.title = `${title} | ${subtitle}`;
|
||||
} else {
|
||||
document.title = subtitle;
|
||||
}
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// НОВЫЕ МЕТОДЫ (добавлены после анализа)
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Форматирование даты с различными опциями
|
||||
* @param {string|Date} date - Дата для форматирования
|
||||
* @param {string} format - 'full' (с временем) или 'short' (без времени)
|
||||
* @returns {string} Отформатированная дата или '—'
|
||||
*/
|
||||
FORMAT_DATE(date, format = 'full') {
|
||||
if (!date) return '—';
|
||||
|
||||
const options = format === 'short'
|
||||
? { day: 'numeric', month: 'short', year: 'numeric' }
|
||||
: { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' };
|
||||
|
||||
return new Date(date).toLocaleDateString('ru-RU', options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Форматирование размера файла в читаемый вид
|
||||
* @param {number} bytes - Размер в байтах
|
||||
* @returns {string} Отформатированный размер (Bytes, KB, MB, GB)
|
||||
*/
|
||||
FORMAT_FILE_SIZE(bytes) {
|
||||
if (!bytes) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
||||
},
|
||||
|
||||
/**
|
||||
* CSS классы для бейджа статуса (активен/неактивен)
|
||||
* @param {boolean} isActive - Статус активности
|
||||
* @returns {string} Tailwind CSS классы для бейджа
|
||||
*/
|
||||
STATUS_BADGE_CLASS(isActive) {
|
||||
const base = 'inline-flex items-center px-2.5 py-1 rounded-md text-xs font-medium border';
|
||||
return isActive
|
||||
? `${base} bg-success/10 text-success border-success/20`
|
||||
: `${base} bg-danger/10 text-danger border-danger/20`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Текстовая метка для статуса публикации
|
||||
* @param {string} status - Статус (verification, published, rejected)
|
||||
* @returns {string} Текстовая метка статуса
|
||||
*/
|
||||
STATUS_LABEL(status) {
|
||||
const labels = {
|
||||
verification: 'На модерации',
|
||||
published: 'Опубликовано',
|
||||
rejected: 'Отклонено',
|
||||
};
|
||||
return labels[status] || status || '—';
|
||||
},
|
||||
|
||||
/**
|
||||
* Разрешение URL для изображений (строка или File объект)
|
||||
* @param {string|File} source - URL/File объект
|
||||
* @returns {string|null} Разрешённый URL или null
|
||||
*/
|
||||
RESOLVE_ASSET_URL(source) {
|
||||
if (!source) return null;
|
||||
|
||||
if (typeof source === 'string') {
|
||||
// Data URL (base64) — возвращаем как есть
|
||||
if (source.startsWith('data:')) return source;
|
||||
// Absolute URL — возвращаем как есть
|
||||
if (source.startsWith('http')) return source;
|
||||
// Relative path — добавляем базовый URL хранилища
|
||||
return this.GET_BASE_STORAGE_URL() + source;
|
||||
}
|
||||
|
||||
// File или Blob объект
|
||||
if (source instanceof File || source instanceof Blob) {
|
||||
return URL.createObjectURL(source);
|
||||
}
|
||||
|
||||
// Неизвестный тип — логируем и возвращаем null
|
||||
console.warn('[RESOLVE_ASSET_URL] Unknown source type:', typeof source, source);
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Валидация PDF файла
|
||||
* @param {File} file - Файл для валидации
|
||||
* @param {number} maxSizeMB - Максимальный размер в MB (по умолчанию 10)
|
||||
* @returns {string|null} Сообщение об ошибке или null
|
||||
*/
|
||||
VALIDATE_PDF(file, maxSizeMB = 10) {
|
||||
if (file.type !== 'application/pdf') {
|
||||
return 'Разрешены только PDF файлы';
|
||||
}
|
||||
|
||||
if (file.size > maxSizeMB * 1024 * 1024) {
|
||||
return `Размер файла не должен превышать ${maxSizeMB}MB`;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Получение инициалов из имени (Фамилия Имя -> ФИ)
|
||||
* @param {string} name - Полное имя
|
||||
* @returns {string} Инициалы (первые 2 буквы)
|
||||
*/
|
||||
GET_INITIALS(name) {
|
||||
if (!name) return '';
|
||||
|
||||
return name
|
||||
.split(' ')
|
||||
.map(n => n[0])
|
||||
.join('')
|
||||
.toUpperCase()
|
||||
.slice(0, 2);
|
||||
},
|
||||
|
||||
/**
|
||||
* Подтверждение и удаление через Inertia
|
||||
* @param {Object} entity - Объект для удаления
|
||||
* @param {string} routeName - Имя роута для удаления
|
||||
* @param {Object} options - Дополнительные опции
|
||||
*/
|
||||
CONFIRM_AND_DELETE(entity, routeName, options = {}) {
|
||||
const message = options.message || `Вы уверены, что хотите удалить "${entity.title || entity.name}"?`;
|
||||
|
||||
if (confirm(message)) {
|
||||
this.$inertia.delete(route(routeName, entity.id), {
|
||||
preserveScroll: options.preserveScroll ?? true,
|
||||
...options.inertiaOptions
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Сброс фильтров и переход к чистой странице
|
||||
* @param {Array<string>} filterKeys - Ключи фильтров для сброса
|
||||
* @param {string} routeName - Имя роута
|
||||
*/
|
||||
RESET_FILTERS(filterKeys, routeName) {
|
||||
filterKeys.forEach(key => {
|
||||
if (Object.prototype.hasOwnProperty.call(this, key)) {
|
||||
this[key] = '';
|
||||
}
|
||||
});
|
||||
|
||||
this.$inertia.get(route(routeName), {}, {
|
||||
preserveState: true,
|
||||
replace: true
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Фильтрация через Inertia с сохранением состояния
|
||||
* @param {string} routeName - Имя роута
|
||||
* @param {Object} params - Параметры фильтрации
|
||||
*/
|
||||
INERTIA_FILTER(routeName, params) {
|
||||
this.$inertia.get(route(routeName), params, {
|
||||
preserveState: true
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Получение базового URL для сущности
|
||||
* @param {string} entityType - Тип сущности (faculty, division и т.д.)
|
||||
* @returns {string} Базовый URL
|
||||
*/
|
||||
GET_ENTITY_BASE_URL(entityType) {
|
||||
return this.GET_BASE_URL() + entityType;
|
||||
},
|
||||
|
||||
/**
|
||||
* Обработка выбора файла через input
|
||||
* @param {Event} event - Событие изменения input
|
||||
* @param {Function} callback - Callback для обработки файла
|
||||
*/
|
||||
HANDLE_FILE_SELECT(event, callback) {
|
||||
const file = event.target?.files?.[0];
|
||||
if (file && callback) {
|
||||
callback(file);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Обработка drag & drop файла
|
||||
* @param {DragEvent} event - Событие drop
|
||||
* @param {Function} callback - Callback для обработки файла
|
||||
*/
|
||||
HANDLE_FILE_DROP(event, callback) {
|
||||
this.isDragging = false;
|
||||
|
||||
const file = event.dataTransfer?.files?.[0];
|
||||
if (file && callback) {
|
||||
callback(file);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Форматирование JSON-полей в читаемый вид
|
||||
* @param {string|Array|Object|null} value - JSON строка или объект
|
||||
* @returns {string|null} Отформатированный текст или null
|
||||
*/
|
||||
FORMAT_JSON_FIELD(value) {
|
||||
if (!value) return null;
|
||||
|
||||
let data = value;
|
||||
if (typeof value === 'string') {
|
||||
try {
|
||||
data = JSON.parse(value);
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
if (data.length === 0) return null;
|
||||
|
||||
const items = data.flatMap(item => {
|
||||
if (typeof item === 'string') return item;
|
||||
if (item && typeof item === 'object') {
|
||||
// Образование: { year, content, institution }
|
||||
if (item.content && item.institution) {
|
||||
return `${item.institution} (${item.year}) — ${item.content}`;
|
||||
}
|
||||
// Публикации: { category_publication, publication: [{ item }] }
|
||||
if (item.publication && Array.isArray(item.publication)) {
|
||||
return item.publication.map(pub => pub.item || pub).filter(Boolean);
|
||||
}
|
||||
// Простой item: { item: "..." }
|
||||
if (item.item) return item.item;
|
||||
// Fallback: все значения объекта
|
||||
return Object.values(item).join(', ');
|
||||
}
|
||||
return String(item);
|
||||
});
|
||||
return items.join('\n');
|
||||
}
|
||||
|
||||
if (data && typeof data === 'object') {
|
||||
// Стаж работы: { total, byProf }
|
||||
if (data.total !== undefined && data.byProf !== undefined) {
|
||||
return `Общий: ${data.total} лет, по профилю: ${data.byProf} лет`;
|
||||
}
|
||||
return Object.entries(data)
|
||||
.map(([key, val]) => `${key}: ${val}`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
return String(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3,12 +3,16 @@
|
||||
export const linksReform = {
|
||||
data() {
|
||||
return {
|
||||
excludedPaths: ['/sveden/', '/storage/', '/upload/', '/panorama/', '/abitur/', '/admin/']
|
||||
excludedPaths: ['sveden', 'storage', 'upload', 'panorama', 'abitur', 'admin']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
normalizePath(path) {
|
||||
return path.replace(/^\/+|\/+$/g, '');
|
||||
},
|
||||
checkPathname(pathname) {
|
||||
return this.excludedPaths.some(path => pathname.startsWith(path));
|
||||
const normalizedPathname = this.normalizePath(pathname);
|
||||
return this.excludedPaths.some(path => normalizedPathname.startsWith(path));
|
||||
},
|
||||
getDefaultLinks() {
|
||||
const links = document.querySelectorAll('a:not([href^="#"]):not([download])');
|
||||
|
||||
Reference in New Issue
Block a user