diff --git a/front_vue/public/config.js b/front_vue/public/config.js index 4391527..31be1f6 100644 --- a/front_vue/public/config.js +++ b/front_vue/public/config.js @@ -6,6 +6,6 @@ window.APP_CONFIG = { IDLE_REFRESH_SECONDS: 1, // Брейкпоинт для мобильной версии (px) - MOBILE_BREAKPOINT: 768 + MOBILE_BREAKPOINT: 1400 } diff --git a/front_vue/src/components/ArchiveCard.vue b/front_vue/src/components/ArchiveCard.vue index fe4ae8a..dfcad16 100644 --- a/front_vue/src/components/ArchiveCard.vue +++ b/front_vue/src/components/ArchiveCard.vue @@ -38,8 +38,12 @@ > {{ cardDepartment.name_departments }} - - + + + {{ card.comments_count }} + + + @@ -96,6 +100,10 @@ > + + + {{ card.comments_count }} + @@ -299,7 +307,8 @@ const closedDateText = computed(() => { letter-spacing: 0.4px; } -.files-badge { +.comments-indicator, +.files-indicator { display: flex; align-items: center; gap: 4px; @@ -307,11 +316,18 @@ const closedDateText = computed(() => { color: var(--text-muted); } -.files-badge i { +.comments-indicator i, +.comments-indicator svg, +.files-indicator i, +.files-indicator svg { width: 14px; height: 14px; } +.indicator-count { + font-size: 11px; +} + /* Аватарка */ .card-assignee { flex-shrink: 0; @@ -373,19 +389,20 @@ const closedDateText = computed(() => { display: flex; align-items: center; justify-content: center; - width: 32px; - height: 32px; - background: rgba(255, 255, 255, 0.08); + width: 24px; + height: 24px; + background: rgba(255, 255, 255, 0.1); border: none; - border-radius: 8px; - color: var(--text-secondary); + border-radius: 6px; + color: var(--text-muted); cursor: pointer; transition: all 0.15s ease; } -.btn-action i { - width: 16px; - height: 16px; +.btn-action i, +.btn-action svg { + width: 18px; + height: 18px; } .btn-restore:hover { @@ -445,22 +462,32 @@ const closedDateText = computed(() => { height: 24px; } -.archive-card.mobile .btn-action i { +.archive-card.mobile .btn-action i, +.archive-card.mobile .btn-action svg { width: 14px; height: 14px; } +.archive-card.mobile .comments-indicator, .archive-card.mobile .files-indicator { display: flex; align-items: center; + gap: 3px; color: var(--text-muted); } -.archive-card.mobile .files-indicator i { +.archive-card.mobile .comments-indicator i, +.archive-card.mobile .comments-indicator svg, +.archive-card.mobile .files-indicator i, +.archive-card.mobile .files-indicator svg { width: 14px; height: 14px; } +.archive-card.mobile .indicator-count { + font-size: 11px; +} + .archive-card.mobile .assignee { width: 24px; height: 24px; diff --git a/front_vue/src/components/Card.vue b/front_vue/src/components/Card.vue index 3c94dea..8ae1479 100644 --- a/front_vue/src/components/Card.vue +++ b/front_vue/src/components/Card.vue @@ -35,6 +35,10 @@ > + + + {{ card.comments_count }} + @@ -405,17 +409,27 @@ const handleArchive = () => { gap: 8px; } +.comments-indicator, .files-indicator { display: flex; align-items: center; + gap: 3px; color: var(--text-muted); } -.files-indicator i { +.comments-indicator i, +.comments-indicator svg, +.files-indicator i, +.files-indicator svg { width: 14px; height: 14px; } +.indicator-count { + font-size: 11px; + font-weight: 500; +} + .due-date { font-size: 11px; color: var(--text-muted); diff --git a/front_vue/src/components/TaskPanel/CommentItem.vue b/front_vue/src/components/TaskPanel/CommentItem.vue index 44d2a02..6aefc95 100644 --- a/front_vue/src/components/TaskPanel/CommentItem.vue +++ b/front_vue/src/components/TaskPanel/CommentItem.vue @@ -3,7 +3,8 @@ class="comment" :class="{ 'comment--own': isOwn, - 'comment--reply': level > 0 + 'comment--reply': level > 0, + 'is-mobile': isMobile }" :style="{ marginLeft: level * 24 + 'px' }" > @@ -14,8 +15,10 @@
- {{ comment.author_name }} - {{ formattedDate }} +
+ {{ comment.author_name }} + {{ formattedDate }} +
diff --git a/front_vue/src/components/TaskPanel/TaskCommentsTab.vue b/front_vue/src/components/TaskPanel/TaskCommentsTab.vue index 6dffc78..0fdff6c 100644 --- a/front_vue/src/components/TaskPanel/TaskCommentsTab.vue +++ b/front_vue/src/components/TaskPanel/TaskCommentsTab.vue @@ -2,10 +2,7 @@
-
- - Загрузка комментариев... -
+
@@ -207,6 +204,7 @@ import CommentForm from './CommentForm.vue' import ConfirmDialog from '../ConfirmDialog.vue' import SlidePanel from '../ui/SlidePanel.vue' import RichTextEditor from '../ui/RichTextEditor.vue' +import Loader from '../ui/Loader.vue' import { commentsApi, commentImageApi, getFullUrl } from '../../api' import { useMobile } from '../../composables/useMobile' @@ -316,14 +314,26 @@ const loadComments = async (silent = false) => { if (hasChanges) { comments.value = newData emit('comments-loaded', newData.length) - await nextTick() - refreshIcons() + + // Для первичной загрузки: убираем loading, ждём рендер, прокручиваем мгновенно + if (!silent) { + loading.value = false + await nextTick() + refreshIcons() + // Мгновенная прокрутка к последнему комментарию + if (commentsListRef.value) { + commentsListRef.value.scrollTop = commentsListRef.value.scrollHeight + } + } else { + await nextTick() + refreshIcons() + } } } } catch (e) { console.error('Ошибка загрузки комментариев:', e) } finally { - if (!silent) { + if (!silent && loading.value) { loading.value = false } } @@ -618,9 +628,11 @@ const previewFile = (file, comment) => { // Хелперы const scrollToBottom = () => { - if (commentsListRef.value) { - commentsListRef.value.scrollTop = commentsListRef.value.scrollHeight - } + setTimeout(() => { + if (commentsListRef.value) { + commentsListRef.value.scrollTop = commentsListRef.value.scrollHeight + } + }, 100) } const refreshIcons = () => { @@ -683,10 +695,11 @@ defineExpose({ diff --git a/front_vue/src/components/ui/SlidePanel.vue b/front_vue/src/components/ui/SlidePanel.vue index d1e980c..f82df57 100644 --- a/front_vue/src/components/ui/SlidePanel.vue +++ b/front_vue/src/components/ui/SlidePanel.vue @@ -354,8 +354,9 @@ onUnmounted(() => { .panel.mobile .panel-body { padding: 16px; padding-bottom: calc(16px + var(--safe-area-bottom, 0px)); - min-height: 0; /* Важно для flex overflow */ - gap: 16px; + min-height: 0; + gap: 0; /* Убираем gap — он создаёт пустое место */ + overflow-y: auto; } .panel.mobile .panel-footer { diff --git a/front_vue/src/views/ArchivePage.vue b/front_vue/src/views/ArchivePage.vue index eb0337e..c9eed7c 100644 --- a/front_vue/src/views/ArchivePage.vue +++ b/front_vue/src/views/ArchivePage.vue @@ -88,10 +88,7 @@
-
- - Загрузка... -
+
@@ -137,6 +134,7 @@ import TaskPanel from '../components/TaskPanel' import ConfirmDialog from '../components/ConfirmDialog.vue' import ProjectSelector from '../components/ProjectSelector.vue' import MobileSelect from '../components/ui/MobileSelect.vue' +import Loader from '../components/ui/Loader.vue' import { useProjectsStore } from '../stores/projects' import { cardsApi } from '../api' import { useMobile } from '../composables/useMobile' @@ -469,30 +467,6 @@ onMounted(async () => { font-size: 13px; } -/* Загрузка */ -.loading-state { - display: flex; - align-items: center; - justify-content: center; - gap: 12px; - padding: 60px 20px; - color: var(--text-muted); -} - -.loading-state i { - width: 20px; - height: 20px; -} - -.spin { - animation: spin 1s linear infinite; -} - -@keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } -} - /* ========== MOBILE ========== */ .app.mobile { flex-direction: column; diff --git a/front_vue/src/views/LoginPage.vue b/front_vue/src/views/LoginPage.vue index 7d8e02d..1e495bb 100644 --- a/front_vue/src/views/LoginPage.vue +++ b/front_vue/src/views/LoginPage.vue @@ -1,5 +1,5 @@