WARNING
Эта статья сгенерирована AI и требует ручной доработки.
Пример: Базовый модуль
В этом разделе мы рассмотрим пример создания базового модуля в FEOD. В качестве примера возьмём модуль управления пользователями (User Management).
Структура модуля
Реализация модуля
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>Преимущества такой организации
- Изоляция — весь код модуля находится в одном месте
- Переиспользуемость — модуль можно легко перенести в другой проект
- Тестируемость — легко тестировать модуль изолированно
- Понятность — ясно, что относится к управлению пользователями
- Масштабируемость — легко добавлять новую функциональность