Исправления фронта
Множество оптимизаций по фронту
This commit is contained in:
166
front_vue/src/composables/useDateFormat.js
Normal file
166
front_vue/src/composables/useDateFormat.js
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* Composable для форматирования дат
|
||||
* Автоматически использует таймзону сервера из serverSettings
|
||||
*
|
||||
* Использование:
|
||||
* import { useDateFormat } from '@/composables/useDateFormat'
|
||||
* const { formatShort, formatRelative, getDaysLeftText } = useDateFormat()
|
||||
*/
|
||||
|
||||
import { serverSettings } from '../api'
|
||||
|
||||
// Константы месяцев
|
||||
const MONTHS_SHORT = ['янв', 'фев', 'мар', 'апр', 'мая', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек']
|
||||
const MONTHS_FULL = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря']
|
||||
|
||||
// Для дат типа "1 май" (именительный падеж)
|
||||
const MONTHS_SHORT_NOM = ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек']
|
||||
|
||||
export function useDateFormat() {
|
||||
|
||||
/**
|
||||
* Парсинг даты с учётом таймзоны сервера
|
||||
*/
|
||||
const parseDate = (dateStr) => {
|
||||
if (!dateStr) return null
|
||||
return serverSettings.parseDate(dateStr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Короткий формат: "1 янв 2025"
|
||||
*/
|
||||
const formatShort = (dateStr) => {
|
||||
const date = parseDate(dateStr)
|
||||
if (!date) return ''
|
||||
const day = date.getDate()
|
||||
return `${day} ${MONTHS_SHORT_NOM[date.getMonth()]} ${date.getFullYear()}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Полный формат: "1 января 2025"
|
||||
*/
|
||||
const formatFull = (dateStr) => {
|
||||
const date = parseDate(dateStr)
|
||||
if (!date) return ''
|
||||
const day = date.getDate()
|
||||
return `${day} ${MONTHS_FULL[date.getMonth()]} ${date.getFullYear()}`
|
||||
}
|
||||
|
||||
/**
|
||||
* С временем: "01 янв 2025, 14:30"
|
||||
*/
|
||||
const formatDateTime = (dateStr) => {
|
||||
const date = parseDate(dateStr)
|
||||
if (!date) return '—'
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const year = date.getFullYear()
|
||||
const hours = date.getHours().toString().padStart(2, '0')
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0')
|
||||
return `${day} ${MONTHS_SHORT[date.getMonth()]} ${year}, ${hours}:${minutes}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Расчёт дней между датами
|
||||
* Положительное число = дней осталось, отрицательное = просрочено
|
||||
*/
|
||||
const getDaysUntil = (dateStr) => {
|
||||
const target = parseDate(dateStr)
|
||||
if (!target) return null
|
||||
const today = new Date()
|
||||
today.setHours(0, 0, 0, 0)
|
||||
target.setHours(0, 0, 0, 0)
|
||||
return Math.round((target - today) / (1000 * 60 * 60 * 24))
|
||||
}
|
||||
|
||||
/**
|
||||
* Расчёт дней назад
|
||||
*/
|
||||
const getDaysAgo = (dateStr) => {
|
||||
const days = getDaysUntil(dateStr)
|
||||
return days !== null ? -days : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Текст для дедлайна: "Сегодня", "Завтра", "Осталось: 5 дн.", "Просрочено: 3 дн."
|
||||
*/
|
||||
const getDaysLeftText = (dateStr) => {
|
||||
const days = getDaysUntil(dateStr)
|
||||
if (days === null) return ''
|
||||
if (days < 0) return `Просрочено: ${Math.abs(days)} дн.`
|
||||
if (days === 0) return 'Сегодня'
|
||||
if (days === 1) return 'Завтра'
|
||||
return `Осталось: ${days} дн.`
|
||||
}
|
||||
|
||||
/**
|
||||
* Статус дедлайна для CSS классов: 'overdue', 'soon', ''
|
||||
*/
|
||||
const getDueDateStatus = (dateStr) => {
|
||||
const days = getDaysUntil(dateStr)
|
||||
if (days === null) return ''
|
||||
if (days < 0) return 'overdue'
|
||||
if (days <= 2) return 'soon'
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Относительная дата в прошлом: "Сегодня", "Вчера", "3 дня назад", или полная дата
|
||||
*/
|
||||
const formatRelative = (dateStr) => {
|
||||
if (!dateStr) return ''
|
||||
const daysAgo = getDaysAgo(dateStr)
|
||||
if (daysAgo === null) return ''
|
||||
|
||||
if (daysAgo === 0) return 'Сегодня'
|
||||
if (daysAgo === 1) return 'Вчера'
|
||||
if (daysAgo >= 2 && daysAgo <= 4) return `${daysAgo} дня назад`
|
||||
if (daysAgo >= 5 && daysAgo <= 14) return `${daysAgo} дней назад`
|
||||
return formatShort(dateStr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Время назад для комментариев: "только что", "5 мин. назад", "2 ч. назад", "3 дн. назад"
|
||||
* Для старых дат — "1 янв 14:30"
|
||||
*/
|
||||
const formatTimeAgo = (dateStr) => {
|
||||
const date = parseDate(dateStr)
|
||||
if (!date) return ''
|
||||
|
||||
const now = new Date()
|
||||
const diff = now - date
|
||||
const minutes = Math.floor(diff / 60000)
|
||||
const hours = Math.floor(diff / 3600000)
|
||||
const days = Math.floor(diff / 86400000)
|
||||
|
||||
if (minutes < 1) return 'только что'
|
||||
if (minutes < 60) return `${minutes} мин. назад`
|
||||
if (hours < 24) return `${hours} ч. назад`
|
||||
if (days < 7) return `${days} дн. назад`
|
||||
|
||||
const day = date.getDate()
|
||||
const time = date.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' })
|
||||
return `${day} ${MONTHS_SHORT[date.getMonth()]} ${time}`
|
||||
}
|
||||
|
||||
return {
|
||||
// Константы
|
||||
MONTHS_SHORT,
|
||||
MONTHS_FULL,
|
||||
|
||||
// Парсинг
|
||||
parseDate,
|
||||
|
||||
// Форматирование
|
||||
formatShort,
|
||||
formatFull,
|
||||
formatDateTime,
|
||||
formatRelative,
|
||||
formatTimeAgo,
|
||||
|
||||
// Расчёты
|
||||
getDaysUntil,
|
||||
getDaysAgo,
|
||||
getDaysLeftText,
|
||||
getDueDateStatus
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user