Шаг 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>