Beta Документация для beta‑теста, возможны ошибки и неточности.
Перейти к содержимому

WARNING

Эта статья сгенерирована AI и требует ручной доработки.

Пример: Базовый модуль

В этом разделе мы рассмотрим пример создания базового модуля в FEOD. В качестве примера возьмём модуль управления пользователями (User Management).

Структура модуля

  • modules
    • UserManagement
      • components
        • UserList.vue
        • UserCard.vue
        • UserForm.vue
      • composables
        • useUsers.ts
        • useUserForm.ts
      • api
        • userApi.ts
      • store
        • userStore.ts
      • types
        • user.types.ts
      • utils
        • formatUserName.ts
      • index.ts

Реализация модуля

1. Типы

ts
// modules/UserManagement/types/user.types.ts
export interface User {
  id: string
  name: string
  email: string
  avatar?: string
  role: 'admin' | 'user' | 'guest'
  createdAt: string
  updatedAt: string
}

export interface CreateUserDto {
  name: string
  email: string
  role: User['role']
}

export interface UpdateUserDto extends Partial<CreateUserDto> {
  id: string
}

2. API

ts
// modules/UserManagement/api/userApi.ts
import type { User, CreateUserDto, UpdateUserDto } from '../types/user.types'

export async function fetchUsers(): Promise<User[]> {
  const response = await fetch('/api/users')
  if (!response.ok) {
    throw new Error('Failed to fetch users')
  }
  return response.json()
}

export async function fetchUserById(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`)
  if (!response.ok) {
    throw new Error('Failed to fetch user')
  }
  return response.json()
}

export async function createUser(data: CreateUserDto): Promise<User> {
  const response = await fetch('/api/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data)
  })
  if (!response.ok) {
    throw new Error('Failed to create user')
  }
  return response.json()
}

export async function updateUser(data: UpdateUserDto): Promise<User> {
  const response = await fetch(`/api/users/${data.id}`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data)
  })
  if (!response.ok) {
    throw new Error('Failed to update user')
  }
  return response.json()
}

export async function deleteUser(id: string): Promise<void> {
  const response = await fetch(`/api/users/${id}`, {
    method: 'DELETE'
  })
  if (!response.ok) {
    throw new Error('Failed to delete user')
  }
}

3. Store

ts
// modules/UserManagement/store/userStore.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { User } from '../types/user.types'
import * as userApi from '../api/userApi'

export const useUserStore = defineStore('user', () => {
  const users = ref<User[]>([])
  const currentUser = ref<User | null>(null)
  const loading = ref(false)
  const error = ref<string | null>(null)

  const usersCount = computed(() => users.value.length)
  const admins = computed(() => users.value.filter(u => u.role === 'admin'))

  async function loadUsers() {
    loading.value = true
    error.value = null
    try {
      users.value = await userApi.fetchUsers()
    } catch (e) {
      error.value = e instanceof Error ? e.message : 'Unknown error'
    } finally {
      loading.value = false
    }
  }

  async function loadUser(id: string) {
    loading.value = true
    error.value = null
    try {
      currentUser.value = await userApi.fetchUserById(id)
    } catch (e) {
      error.value = e instanceof Error ? e.message : 'Unknown error'
    } finally {
      loading.value = false
    }
  }

  async function addUser(data: CreateUserDto) {
    loading.value = true
    error.value = null
    try {
      const newUser = await userApi.createUser(data)
      users.value.push(newUser)
      return newUser
    } catch (e) {
      error.value = e instanceof Error ? e.message : 'Unknown error'
      throw e
    } finally {
      loading.value = false
    }
  }

  async function updateUserData(data: UpdateUserDto) {
    loading.value = true
    error.value = null
    try {
      const updatedUser = await userApi.updateUser(data)
      const index = users.value.findIndex(u => u.id === data.id)
      if (index !== -1) {
        users.value[index] = updatedUser
      }
      if (currentUser.value?.id === data.id) {
        currentUser.value = updatedUser
      }
      return updatedUser
    } catch (e) {
      error.value = e instanceof Error ? e.message : 'Unknown error'
      throw e
    } finally {
      loading.value = false
    }
  }

  async function removeUser(id: string) {
    loading.value = true
    error.value = null
    try {
      await userApi.deleteUser(id)
      users.value = users.value.filter(u => u.id !== id)
      if (currentUser.value?.id === id) {
        currentUser.value = null
      }
    } catch (e) {
      error.value = e instanceof Error ? e.message : 'Unknown error'
      throw e
    } finally {
      loading.value = false
    }
  }

  return {
    users,
    currentUser,
    loading,
    error,
    usersCount,
    admins,
    loadUsers,
    loadUser,
    addUser,
    updateUserData,
    removeUser
  }
})

4. Composables

ts
// modules/UserManagement/composables/useUsers.ts
import { onMounted } from 'vue'
import { useUserStore } from '../store/userStore'

export function useUsers() {
  const store = useUserStore()

  onMounted(() => {
    if (store.users.length === 0) {
      store.loadUsers()
    }
  })

  return {
    users: store.users,
    loading: store.loading,
    error: store.error,
    usersCount: store.usersCount,
    admins: store.admins,
    loadUsers: store.loadUsers,
    refreshUsers: store.loadUsers
  }
}
ts
// modules/UserManagement/composables/useUserForm.ts
import { ref } from 'vue'
import type { CreateUserDto, UpdateUserDto } from '../types/user.types'
import { useUserStore } from '../store/userStore'

export function useUserForm() {
  const store = useUserStore()
  const isSubmitting = ref(false)

  async function createUser(data: CreateUserDto) {
    isSubmitting.value = true
    try {
      await store.addUser(data)
      return true
    } catch (error) {
      console.error('Failed to create user:', error)
      return false
    } finally {
      isSubmitting.value = false
    }
  }

  async function updateUser(data: UpdateUserDto) {
    isSubmitting.value = true
    try {
      await store.updateUserData(data)
      return true
    } catch (error) {
      console.error('Failed to update user:', error)
      return false
    } finally {
      isSubmitting.value = false
    }
  }

  return {
    isSubmitting,
    createUser,
    updateUser
  }
}

5. Компоненты

vue
<!-- modules/UserManagement/components/UserList.vue -->
<template>
  <div class="user-list">
    <div v-if="loading">Загрузка...</div>
    <div v-else-if="error">{{ error }}</div>
    <div v-else>
      <UserCard
        v-for="user in users"
        :key="user.id"
        :user="user"
        @delete="handleDelete"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { useUsers } from '../composables/useUsers'
import { useUserStore } from '../store/userStore'
import UserCard from './UserCard.vue'

const { users, loading, error } = useUsers()
const store = useUserStore()

function handleDelete(id: string) {
  store.removeUser(id)
}
</script>
vue
<!-- modules/UserManagement/components/UserForm.vue -->
<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="form.name" placeholder="Имя" required />
    <input v-model="form.email" type="email" placeholder="Email" required />
    <select v-model="form.role" required>
      <option value="user">Пользователь</option>
      <option value="admin">Администратор</option>
      <option value="guest">Гость</option>
    </select>
    <button type="submit" :disabled="isSubmitting">
      {{ isEditing ? 'Обновить' : 'Создать' }}
    </button>
  </form>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import type { User, CreateUserDto, UpdateUserDto } from '../types/user.types'
import { useUserForm } from '../composables/useUserForm'

const props = defineProps<{
  user?: User
}>()

const emit = defineEmits<{
  success: []
}>()

const { isSubmitting, createUser, updateUser } = useUserForm()

const form = ref<CreateUserDto>({
  name: '',
  email: '',
  role: 'user'
})

const isEditing = ref(false)

watch(() => props.user, (user) => {
  if (user) {
    isEditing.value = true
    form.value = {
      name: user.name,
      email: user.email,
      role: user.role
    }
  } else {
    isEditing.value = false
    form.value = {
      name: '',
      email: '',
      role: 'user'
    }
  }
}, { immediate: true })

async function handleSubmit() {
  if (isEditing.value && props.user) {
    const success = await updateUser({
      id: props.user.id,
      ...form.value
    })
    if (success) {
      emit('success')
    }
  } else {
    const success = await createUser(form.value)
    if (success) {
      emit('success')
    }
  }
}
</script>

6. Утилиты

ts
// modules/UserManagement/utils/formatUserName.ts
import type { User } from '../types/user.types'

export function formatUserName(user: User): string {
  return `${user.name} (${user.email})`
}

export function formatUserRole(user: User): string {
  const roleMap = {
    admin: 'Администратор',
    user: 'Пользователь',
    guest: 'Гость'
  }
  return roleMap[user.role] || user.role
}

7. Публичный API модуля

ts
// modules/UserManagement/index.ts
// Компоненты
export { default as UserList } from './components/UserList.vue'
export { default as UserCard } from './components/UserCard.vue'
export { default as UserForm } from './components/UserForm.vue'

// Composables
export { useUsers } from './composables/useUsers'
export { useUserForm } from './composables/useUserForm'

// Store
export { useUserStore } from './store/userStore'

// API (обычно не экспортируем, но можно для специальных случаев)
export * as userApi from './api/userApi'

// Типы
export type { User, CreateUserDto, UpdateUserDto } from './types/user.types'

// Утилиты
export { formatUserName, formatUserRole } from './utils/formatUserName'

Использование модуля

vue
<!-- pages/users.vue -->
<template>
  <div>
    <h1>Управление пользователями</h1>
    <UserForm @success="handleSuccess" />
    <UserList />
  </div>
</template>

<script setup lang="ts">
import { UserForm, UserList, useUserStore } from '@/modules/UserManagement'

const store = useUserStore()

function handleSuccess() {
  store.loadUsers()
}
</script>

Преимущества такой организации

  1. Изоляция — весь код модуля находится в одном месте
  2. Переиспользуемость — модуль можно легко перенести в другой проект
  3. Тестируемость — легко тестировать модуль изолированно
  4. Понятность — ясно, что относится к управлению пользователями
  5. Масштабируемость — легко добавлять новую функциональность