1
0

Мобильная версия

1. Адаптация и разработка мобильного варианта.
This commit is contained in:
2026-01-15 09:33:16 +07:00
parent 901604afd4
commit 5018a2d123
23 changed files with 2787 additions and 171 deletions

View File

@@ -1,6 +1,6 @@
<template>
<div class="board">
<div class="columns">
<div class="board" :class="{ mobile: isMobile }">
<div class="columns" ref="columnsRef" @scroll="onColumnsScroll">
<Column
v-for="column in filteredColumns"
:key="column.id"
@@ -14,6 +14,21 @@
@archive-task="archiveTask"
/>
</div>
<!-- Мобильный индикатор снизу (над навигацией) -->
<div v-if="isMobile" class="mobile-column-footer">
<div class="current-column-title">{{ currentColumnTitle }}</div>
<div class="column-indicators">
<button
v-for="(column, index) in filteredColumns"
:key="column.id"
class="indicator-dot"
:class="{ active: currentColumnIndex === index }"
:style="{ '--dot-color': column.color }"
@click="scrollToColumn(index)"
></button>
</div>
</div>
</div>
</template>
@@ -21,6 +36,35 @@
import { ref, computed, onMounted, onUpdated, watch } from 'vue'
import Column from './Column.vue'
import { cardsApi } from '../api'
import { useMobile } from '../composables/useMobile'
const { isMobile } = useMobile()
// Мобильный свайп
const columnsRef = ref(null)
const currentColumnIndex = ref(0)
const onColumnsScroll = () => {
if (!columnsRef.value || !isMobile.value) return
const scrollLeft = columnsRef.value.scrollLeft
const columnWidth = columnsRef.value.scrollWidth / filteredColumns.value.length
currentColumnIndex.value = Math.round(scrollLeft / columnWidth)
}
const scrollToColumn = (index) => {
if (!columnsRef.value) return
const columnWidth = columnsRef.value.scrollWidth / filteredColumns.value.length
columnsRef.value.scrollTo({
left: index * columnWidth,
behavior: 'smooth'
})
}
// Название текущей колонки для мобильного индикатора
const currentColumnTitle = computed(() => {
const col = filteredColumns.value[currentColumnIndex.value]
return col ? col.title : ''
})
const props = defineProps({
activeDepartment: Number,
@@ -277,4 +321,76 @@ defineExpose({ saveTask, deleteTask, archiveTask })
align-items: flex-start;
}
/* ========== MOBILE: колонки со свайпом ========== */
.board.mobile {
min-width: auto;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.board.mobile .columns {
flex: 1;
display: flex;
gap: 0;
padding: 0;
overflow-x: auto;
overflow-y: hidden;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
-ms-overflow-style: none;
/* Отключаем вертикальный скролл на уровне этого элемента */
overscroll-behavior: contain;
touch-action: pan-x;
}
.board.mobile .columns::-webkit-scrollbar {
display: none;
}
/* Мобильный футер с индикатором колонок */
.mobile-column-footer {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: var(--bg-body);
flex-shrink: 0;
min-height: 60px;
}
.current-column-title {
font-size: 13px;
font-weight: 500;
color: var(--text-secondary);
text-align: center;
}
.column-indicators {
display: flex;
justify-content: center;
gap: 8px;
}
.indicator-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
border: none;
padding: 0;
cursor: pointer;
transition: all 0.3s ease;
}
.indicator-dot.active {
width: 28px;
border-radius: 5px;
background: var(--dot-color, var(--accent));
}
</style>