Шаг 1. Добавление списка пользователей
На этапе вёрстки мы ещё не знали, как работать с динамическими данными в шаблоне. Сейчас самое время доработать шаблон, используя данные из мок-файлов.
Цель этого шага — обновить список пользователей. Первым делом необходимо импортировать пользователей в файле HomeView.vue. Добавьте блок script setup и выполните импорт.
<script setup>
import users from '../mocks/users.json'
</script>
После этого пользователи станут доступны в шаблоне. Помимо этого, понадобится вспомогательная функция для работы с аватарами пользователей.
<script setup>
import users from '../mocks/users.json'
const getImage = image => {
// https://vitejs.dev/guide/assets.html#new-url-url-import-meta-url
return new URL(`../assets/img/${image}`, import.meta.url).href
}
</script>
Найдите в шаблоне строку <ul class="user-filter"> и замените этот элемент вместе с его содержимым на динамический список пользователей:
<ul class="user-filter">
<li
v-for="user in users"
:key="user.id"
:title="user.name"
class="user-filter__item"
>
<a class="user-filter__button">
<img
:src="getImage(user.avatar)"
alt="Аватар юзера"
width="24"
height="24"
/>
</a>
</li>
</ul>
Теперь пользователи берутся из файла users.json. В дальнейших разделах мы заменим этот подход и будем получать пользователей с сервера.
Шаг 2. Добавление списка статусов
Для обновления списка статусов необходимо импортировать его и вывести в шаблоне.
<script setup>
import users from '../mocks/users.json'
import {STATUSES} from '../common/constants'
const getImage = image => {
// https://vitejs.dev/guide/assets.html#new-url-url-import-meta-url
return new URL(`../assets/img/${image}`, import.meta.url).href
}
</script>
Замените элемент <ul class="meta-filter"> на следующий код:
<ul class="meta-filter">
<li
v-for="({ value, label }) in STATUSES"
:key="value"
class="meta-filter__item"
>
<a
class="meta-filter__status"
:class="`meta-filter__status meta-filter__status--color meta-filter__status--${value}`"
:title="label"
/>
</li>
</ul>
Шаг 3. Добавление динамических колонок
Сначала выполним импорт колонок из файла src/mocks/columns.json.
<script setup>
import columns from '../mocks/columns.json'
import users from '../mocks/users.json'
import {STATUSES} from '../common/constants'
const getImage = image => {
// https://vitejs.dev/guide/assets.html#new-url-url-import-meta-url
return new URL(`../assets/img/${image}`, import.meta.url).href
}
</script>
Если на доске присутствуют колонки, их нужно отрисовать. В противном случае следует отобразить сообщение о том, что доска пуста.
Добавим директиву v-if к блоку <div class="desk__columns">.
<div v-if="columns.length" class="desk__columns">
Это условие проверяет наличие элементов в массиве колонок. Когда колонок нет, необходимо вывести соответствующее уведомление.
Замените блок <p class="desk__emptiness"> на:
<p
v-else
class="desk__emptiness"
>
Обратите внимание, что блоки <div class="desk__columns"> и <p class="desk__emptiness"> расположены на одном уровне вложенности.
Когда имеется хотя бы одна колонка, с помощью директивы v-for мы отрисуем все колонки и принадлежащие им задачи.
Найдите следующий фрагмент:
<div class="column">
<h2 class="column__name">Название колонки</h2>
<div class="column__target-area">
И замените его на:
<div v-for="column in columns" :key="column.id" class="column">
<h2 class="column__name">{{ column.title }}</h2>
<div class="column__target-area">
Колонки теперь формируются динамически.
Шаг 4. Добавление динамических задач
Импортируем задачи, а также вспомогательные функции и константы:
<script setup>
import columns from '../mocks/columns.json'
import users from '../mocks/users.json'
import rawTasks from '../mocks/tasks.json'
import {normalizeTask, getTagsArrayFromString} from '../common/helpers'
import {STATUSES} from '../common/constants'
const getImage = image => {
// https://vitejs.dev/guide/assets.html#new-url-url-import-meta-url
return new URL(`../assets/img/${image}`, import.meta.url).href
}
</script>
Выполним нормализацию задач:
const normalizedTasks = rawTasks.map(task => normalizeTask(task))
Создадим функцию для распределения задач по колонкам:
const columnTasks = normalizedTasks
// Фильтруем задачи, которые прикреплены к колонке
.filter(({ columnId }) => columnId)
.reduce((accumulator, task) => {
task.tags = getTagsArrayFromString(task.tags)
if (accumulator[task.columnId]) {
accumulator[task.columnId] = [...accumulator[task.columnId], task]
} else {
accumulator[task.columnId] = [task]
}
return accumulator
}, {})
Найдите элемент <div class="column__task"> и замените его на следующий код:
<div
v-for="task in columnTasks[column.id]"
:key="task.id"
class="column__task"
>
<div class="task">
<div
v-if="task.user"
class="task__user"
>
<div class="task__avatar">
<img
:src="getImage(task.user.avatar)"
:alt="task.user.name"
width="20"
height="20"
/>
</div>
{{ task.user.name }}
</div>
<div class="task__statuses">
<span
v-if="task.status"
class="task__status"
:class="`task__status--${task.status}`"
/>
<span
v-if="task.timeStatus"
class="task__status"
:class="`task__status--${task.timeStatus}`"
/>
</div>
<h5
class="task__title"
:class="{'task__title--first': !task.user}"
>
{{ task.title }}
</h5>
<ul
v-if="task.tags && task.tags.length"
class="task__tags"
>
<li
v-for="(tag, index) in task.tags"
:key="index"
>
<span class="tag tag--blue">
{{ tag }}
</span>
</li>
</ul>
</div>
</div>
Шаг 5. Окончательная версия файла HomeView.vue без стилей
<template>
<main class="content">
<section class="desk">
<!-- Шапка доски-->
<div class="desk__header">
<h1 class="desk__title">Design Coffee Lab</h1>
<div class="desk__filters">
<div class="desk__user-filter">
<!-- Список пользователей-->
<ul class="user-filter">
<li
v-for="user in users"
:key="user.id"
:title="user.name"
class="user-filter__item"
>
<a class="user-filter__button">
<img
:src="getImage(user.avatar)"
alt="Аватар юзера"
width="24"
height="24"
/>
</a>
</li>
</ul>
</div>
<div class="desk__meta-filter">
<!-- Список статусов-->
<ul class="meta-filter">
<li
v-for="({ value, label }) in STATUSES"
:key="value"
class="meta-filter__item"
>
<a
class="meta-filter__status"
:class="`meta-filter__status meta-filter__status--color meta-filter__status--${value}`"
:title="label"
/>
</li>
</ul>
</div>
</div>
</div>
<!-- Колонки и задачи-->
<div v-if="columns.length" class="desk__columns">
<div v-for="column in columns" :key="column.id" class="column">
<h2 class="column__name">{{ column.title }}</h2>
<div class="column__target-area">
<!-- Задачи-->
<div
v-for="task in columnTasks[column.id]"
:key="task.id"
class="column__task"
>
<div class="task">
<div
v-if="task.user"
class="task__user"
>
<div class="task__avatar">
<img
:src="getImage(task.user.avatar)"
:alt="task.user.name"
width="20"
height="20"
/>
</div>
{{ task.user.name }}
</div>
<div class="task__statuses">
<span
v-if="task.status"
class="task__status"
:class="`task__status--${task.status}`"
/>
<span
v-if="task.timeStatus"
class="task__status"
:class="`task__status--${task.timeStatus}`"
/>
</div>
<h5
class="task__title"
:class="{ 'task__title--first': !task.user }"
>
{{ task.title }}
</h5>
<ul
v-if="task.tags && task.tags.length"
class="task__tags"
>
<li
v-for="(tag, index) in task.tags"
:key="index"
>
<span class="tag tag--blue">
{{ tag }}
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Пустая доска-->
<p
v-else
class="desk__emptiness"
>
Пока нет ни одной колонки
</p>
</section>
</main>
</template>
<script setup>
import columns from '../mocks/columns.json'
import users from '../mocks/users.json'
import rawTasks from '../mocks/tasks.json'
import { normalizeTask, getTagsArrayFromString } from '../common/helpers'
import { STATUSES } from '../common/constants'
const normalizedTasks = rawTasks.map(task => normalizeTask(task))
const columnTasks = normalizedTasks
.filter(({ columnId }) => columnId)
.reduce((accumulator, task) => {
task.tags = getTagsArrayFromString(task.tags)
if (accumulator[task.columnId]) {
accumulator[task.columnId] = [...accumulator[task.columnId], task]
} else {
accumulator[task.columnId] = [task]
}
return accumulator
}, {})
const getImage = image => {
// https://vitejs.dev/guide/assets.html#new-url-url-import-meta-url
return new URL(`../assets/img/${image}`, import.meta.url).href
}
</script>
Шаг 6. Подключение шаблона и доски задач к App.vue
Чтобы увидеть результат, нужно подключить компоненты AppLayout и HomeView в корневом компоненте.
Замените содержимое файла App.vue на следующее:
<template>
<app-layout>
<home-view />
</app-layout>
</template>
<script setup>
import { AppLayout } from "@/layouts";
import { HomeView } from "@/views";
</script>