Files
2026-04-07 17:54:26 +05:00

8.1 KiB

Posts Form Components Guide

Структура компонентов

Posts/Form.vue (1284 строки) разбит на переиспользуемые подкомпоненты:

Posts/Components/
├── PostMainInfo.vue           # Заголовок, slug, статус, категория
├── PostTagsAuthors.vue        # Теги и авторы
└── PostPublishSettings.vue    # Настройки публикации и соцсети

Как использовать

1. PostMainInfo.vue

Основная информация о посте.

<PostMainInfo
  v-model:title="form.title"
  v-model:slug="form.slug"
  v-model:status="form.status"
  v-model:category-id="form.category_id"
  :categories="categories"
  :status-options="[PostStatus.DRAFT, PostStatus.PUBLISHED, PostStatus.VERIFICATION, PostStatus.REJECTED]"
  :errors="form.errors"
  @generate-slug="generateSlug"
/>

Props:

  • title - заголовок новости
  • slug - URL-адрес
  • status - статус публикации
  • categoryId - ID категории
  • categories - массив категорий
  • statusOptions - массив опций статуса
  • errors - объект ошибок валидации
  • inputClass - CSS классы для инпутов

Events:

  • update:title - обновление заголовка
  • update:slug - обновление slug
  • update:status - обновление статуса
  • update:categoryId - обновление категории
  • generate-slug - генерация slug из заголовка

2. PostTagsAuthors.vue

Управление тегами и авторами.

<PostTagsAuthors
  :tags="form.tags"
  :authors="form.authors"
  @add-tag="addTag"
  @remove-tag="removeTag"
  @add-author="addAuthor"
  @remove-author="removeAuthor"
/>

Props:

  • tags - массив тегов
  • authors - массив авторов

Events:

  • add-tag - добавить тег
  • remove-tag - удалить тег по индексу
  • add-author - добавить автора
  • remove-author - удалить автора по индексу

3. PostPublishSettings.vue

Настройки публикации и социальных сетей.

<PostPublishSettings
  v-model:publish-after="form.publish_setting.publish_after"
  v-model:publish-at="form.publish_setting.publish_at"
  v-model:publish-vk="form.publication.vk"
  :errors="form.errors"
/>

Props:

  • publishAfter - флаг отложенной публикации
  • publishAt - дата/время публикации
  • publishVk - публикация ВКонтакте
  • errors - объект ошибок валидации
  • inputClass - CSS классы для инпутов

Events:

  • update:publishAfter - обновление флага отложенной публикации
  • update:publishAt - обновление даты/времени
  • update:publishVk - обновление флага VK

Пример интеграции в Form.vue

BEFORE (монолитный подход):

<!-- Внутри Form.vue - 1284 строки -->
<div class="space-y-6">
  <!-- Title & Slug -->
  <div class="grid grid-cols-1 md:grid-cols-2 gap-5">
    <!-- 50+ строк markup -->
  </div>
  
  <!-- Status & Category -->
  <div class="grid grid-cols-1 md:grid-cols-2 gap-5">
    <!-- 40+ строк markup -->
  </div>
  
  <!-- Tags -->
  <div>
    <!-- 60+ строк markup -->
  </div>
  
  <!-- Authors -->
  <div>
    <!-- 60+ строк markup -->
  </div>
  
  <!-- Publishing Settings -->
  <div class="border border-layer-line rounded-lg overflow-hidden">
    <!-- 80+ строк markup -->
  </div>
  
  <!-- Social Media -->
  <div class="border border-layer-line rounded-lg overflow-hidden">
    <!-- 50+ строк markup -->
  </div>
</div>

AFTER (компонентный подход):

<template>
  <div class="min-h-screen bg-background-2">
    <!-- Sticky Header -->
    <div class="border-b border-line-2 bg-layer/50 backdrop-blur-sm sticky top-0 z-20">
      <!-- Header content -->
    </div>

    <!-- Main Content -->
    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
      <FlashMessages />
      
      <form @submit.prevent="submitForm" class="space-y-6">
        <!-- Tabs Navigation -->
        <div class="bg-layer border border-layer-line rounded-lg shadow-xs overflow-hidden">
          <div class="border-b border-line-2">
            <!-- Tab buttons -->
          </div>

          <!-- Tab Content -->
          <div class="p-6">
            <!-- Tab: Main Info -->
            <div v-show="activeTab === 'main'" class="space-y-6">
              <PostMainInfo
                v-model:title="form.title"
                v-model:slug="form.slug"
                v-model:status="form.status"
                v-model:category-id="form.category_id"
                :categories="categories"
                :status-options="statusOptions"
                :errors="form.errors"
                @generate-slug="generateSlug"
              />
              
              <PostTagsAuthors
                :tags="form.tags"
                :authors="form.authors"
                @add-tag="addTag"
                @remove-tag="removeTag"
                @add-author="addAuthor"
                @remove-author="removeAuthor"
              />
              
              <PostPublishSettings
                v-model:publish-after="form.publish_setting.publish_after"
                v-model:publish-at="form.publish_setting.publish_at"
                v-model:publish-vk="form.publication.vk"
                :errors="form.errors"
              />
            </div>

            <!-- Tab: Content -->
            <div v-show="activeTab === 'content'">
              <!-- Content builder stays as is -->
            </div>

            <!-- Tab: Media -->
            <div v-show="activeTab === 'media'">
              <!-- Media management -->
            </div>

            <!-- Tab: Slider -->
            <div v-show="activeTab === 'slider'">
              <!-- Slider settings -->
            </div>
          </div>
        </div>
      </form>
    </div>
  </div>
</template>

<script>
import PostMainInfo from './Components/PostMainInfo.vue';
import PostTagsAuthors from './Components/PostTagsAuthors.vue';
import PostPublishSettings from './Components/PostPublishSettings.vue';

export default {
  components: {
    PostMainInfo,
    PostTagsAuthors,
    PostPublishSettings
  },
  
  methods: {
    addTag(tag) {
      this.form.tags.push(tag);
    },
    removeTag(index) {
      this.form.tags.splice(index, 1);
    },
    addAuthor(author) {
      this.form.authors.push(author);
    },
    removeAuthor(index) {
      this.form.authors.splice(index, 1);
    },
    generateSlug() {
      if (!this.form.slug && this.form.title) {
        this.form.slug = this.slugify(this.form.title);
      }
    }
  }
}
</script>

Преимущества рефакторинга

До:

  • 1284 строки в одном файле
  • Сложно искать и отлаживать
  • Невозможно переиспользовать
  • Долгая загрузка

После:

  • ~300-400 строк в Form.vue (только логика)
  • 3 переиспользуемых компонента (~150 строк каждый)
  • Легко тестировать изолированно
  • Можно использовать в других формах (например, редактирование новостей)
  • Улучшенная читаемость

Следующие шаги

  1. Заменить в Form.vue использование этих компонентов
  2. Создать дополнительные компоненты:
    • PostContentBuilder.vue - конструктор контента
    • PostMediaUploader.vue - загрузка медиа
    • PostSliderConfig.vue - настройка слайдера
  3. Протестировать все сценарии создания/редактирования