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,5 +1,5 @@
<template>
<div class="app">
<div class="app" :class="{ mobile: isMobile }">
<!-- Боковая панель навигации -->
<Sidebar />
@@ -33,6 +33,27 @@
</button>
</div>
</template>
<!-- Мобильные фильтры -->
<template #mobile-filters>
<MobileSelect
v-model="mobileProjectId"
:options="projectOptions"
title="Выберите проект"
placeholder="Проект"
variant="accent"
@update:model-value="onMobileProjectChange"
/>
<MobileSelect
v-model="activeDepartment"
:options="departmentOptions"
title="Фильтр по отделам"
placeholder="Все отделы"
icon="filter"
compact
/>
</template>
<template #stats>
<div class="header-stats">
<div class="stat">
@@ -45,6 +66,15 @@
<!-- Список архивных задач -->
<main class="main">
<!-- Мобильный заголовок над карточками -->
<div v-if="isMobile" class="mobile-archive-header">
<div class="archive-title-row">
<span class="archive-dot"></span>
<span class="archive-title">В архиве</span>
<span class="archive-count">{{ filteredCards.length }}</span>
</div>
</div>
<div class="archive-list">
<ArchiveCard
v-for="card in filteredCards"
@@ -73,6 +103,7 @@
</main>
</div>
<!-- Панель редактирования задачи -->
<TaskPanel
:show="panelOpen"
@@ -110,12 +141,45 @@ import ArchiveCard from '../components/ArchiveCard.vue'
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 { useProjectsStore } from '../stores/projects'
import { cardsApi } from '../api'
import { useMobile } from '../composables/useMobile'
const { isMobile } = useMobile()
// ==================== STORE ====================
const store = useProjectsStore()
// ==================== MOBILE ====================
const mobileProjectId = computed({
get: () => store.currentProjectId,
set: () => {}
})
const projectOptions = computed(() => {
return store.projects.map(p => ({
id: p.id,
label: p.name
}))
})
const departmentOptions = computed(() => {
return [
{ id: null, label: 'Все отделы' },
...store.departments.map(d => ({
id: d.id,
label: d.name_departments
}))
]
})
const onMobileProjectChange = async (projectId) => {
await store.setCurrentProject(projectId)
activeDepartment.value = null
await fetchCards()
}
// ==================== КАРТОЧКИ ====================
const cards = ref([])
const loading = ref(true)
@@ -437,4 +501,92 @@ onMounted(async () => {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* ========== MOBILE ========== */
.app.mobile {
flex-direction: column;
height: 100vh;
height: 100dvh;
overflow: hidden;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.app.mobile .main-wrapper {
margin-left: 0;
flex: 1;
min-height: 0;
padding-bottom: calc(64px + var(--safe-area-bottom, 0px));
overflow: hidden;
display: flex;
flex-direction: column;
}
.app.mobile .main {
flex: 1;
min-height: 0;
padding: 0 16px 16px;
overflow: hidden;
}
.app.mobile .archive-list {
/* 60px header + 40px title + 64px nav + safe-area */
max-height: calc(100dvh - 60px - 40px - 64px - var(--safe-area-bottom, 0px));
overflow-y: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
-ms-overflow-style: none;
padding-bottom: 16px;
}
.app.mobile .archive-list::-webkit-scrollbar {
display: none;
}
.app.mobile .archive-list {
max-width: none;
gap: 12px;
}
.app.mobile .empty-state {
padding: 40px 20px;
}
.app.mobile .empty-state i {
width: 40px;
height: 40px;
}
/* Мобильный заголовок над карточками */
.mobile-archive-header {
padding: 12px 0 16px 4px;
}
.archive-title-row {
display: flex;
align-items: center;
gap: 8px;
}
.archive-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--orange, #ff9f43);
}
.archive-title {
font-size: 14px;
font-weight: 500;
color: var(--text-secondary);
}
.archive-count {
font-size: 14px;
font-weight: 500;
color: var(--text-muted);
}
</style>