# Vue.js 3 Rules

## Component Structure

### Template

```vue
<template>
  <!-- ✅ Логическая структура -->
  <div class="container">
    <header>
      <h2>{{ title }}</h2>
    </header>
    
    <main>
      <DataTable :value="items" />
    </main>
    
    <footer>
      <Button @click="handleSave">Save</Button>
    </footer>
  </div>
</template>
```

### Script Setup

```vue
<script setup>
// ✅ Всегда используй <script setup>
import { ref, computed, onMounted } from 'vue';
import DataTable from 'primevue/datatable';
import { apiGet } from '@/utils/http.js';

// Props
const props = defineProps({
  title: {
    type: String,
    required: true
  }
});

// Emits
const emit = defineEmits(['update', 'delete']);

// State
const items = ref([]);
const loading = ref(false);

// Computed
const totalItems = computed(() => items.value.length);

// Methods
async function loadItems() {
  loading.value = true;
  try {
    const result = await apiGet('getItems');
    items.value = result.data || [];
  } finally {
    loading.value = false;
  }
}

// Lifecycle
onMounted(() => {
  loadItems();
});
</script>
```

### Styles

```vue
<style scoped>
/* ✅ Используй scoped стили */
.container {
  padding: 1rem;
}

/* ✅ Используй :deep() для доступа к дочерним компонентам */
:deep(.p-datatable) {
  border: 1px solid #ccc;
}
</style>
```

## Component Naming

```vue
<!-- ✅ PascalCase для компонентов -->
<CustomerCard />
<ProductsList />
<OrderDetails />

<!-- ✅ kebab-case в шаблоне тоже работает -->
<customer-card />
```

## Props

```vue
<script setup>
// ✅ Всегда определяй типы и валидацию
const props = defineProps({
  customerId: {
    type: Number,
    required: true,
    validator: (value) => value > 0
  },
  showDetails: {
    type: Boolean,
    default: false
  },
  items: {
    type: Array,
    default: () => []
  }
});
</script>
```

## Emits

```vue
<script setup>
// ✅ Определяй emits с типами
const emit = defineEmits<{
  update: [id: number, data: object];
  delete: [id: number];
  cancel: [];
}>();

// ✅ Или с валидацией
const emit = defineEmits({
  update: (id: number, data: object) => {
    if (id > 0 && typeof data === 'object') {
      return true;
    }
    console.warn('Invalid emit arguments');
    return false;
  }
});
</script>
```

## Reactive State

```vue
<script setup>
// ✅ ref для примитивов
const count = ref(0);
const name = ref('');

// ✅ ref для объектов (предпочтительно)
const customer = ref({
  id: null,
  name: '',
  email: ''
});

// ✅ reactive только если нужно
import { reactive } from 'vue';
const state = reactive({
  items: [],
  loading: false
});
</script>
```

## Computed Properties

```vue
<script setup>
// ✅ Используй computed для производных значений
const filteredItems = computed(() => {
  return items.value.filter(item => item.isActive);
});

// ✅ Computed с getter/setter
const fullName = computed({
  get: () => `${firstName.value} ${lastName.value}`,
  set: (value) => {
    const parts = value.split(' ');
    firstName.value = parts[0];
    lastName.value = parts[1];
  }
});
</script>
```

## Event Handlers

```vue
<template>
  <!-- ✅ Используй kebab-case для событий -->
  <Button @click="handleClick" />
  <Input @input="handleInput" />
  <Form @submit.prevent="handleSubmit" />
</template>

<script setup>
// ✅ Именуй обработчики с префиксом handle
function handleClick() {
  // ...
}

function handleInput(event) {
  // ...
}

function handleSubmit() {
  // ...
}
</script>
```

## Conditional Rendering

```vue
<template>
  <!-- ✅ Используй v-if для условного рендеринга -->
  <div v-if="loading">
    <LoadingSpinner />
  </div>
  
  <!-- ✅ v-show для частых переключений -->
  <div v-show="hasItems">
    <ItemsList :items="items" />
  </div>
  
  <!-- ✅ v-else для альтернатив -->
  <div v-else>
    <EmptyState />
  </div>
</template>
```

## Lists

```vue
<template>
  <!-- ✅ Всегда используй :key -->
  <div v-for="item in items" :key="item.id">
    {{ item.name }}
  </div>
  
  <!-- ✅ Для индексов -->
  <div v-for="(item, index) in items" :key="`item-${index}`">
    {{ item.name }}
  </div>
</template>
```

## Form Handling

```vue
<template>
  <form @submit.prevent="handleSubmit">
    <!-- ✅ Используй v-model -->
    <InputText v-model="form.name" />
    <Textarea v-model="form.description" />
    
    <!-- ✅ Для кастомных компонентов -->
    <CustomInput v-model="form.email" />
  </form>
</template>

<script setup>
const form = ref({
  name: '',
  description: '',
  email: ''
});

function handleSubmit() {
  // Валидация и отправка
}
</script>
```

## PrimeVue Components

```vue
<template>
  <!-- ✅ Используй PrimeVue компоненты в админке -->
  <DataTable
    :value="customers"
    :loading="loading"
    paginator
    :rows="20"
    @page="onPage"
  >
    <Column field="name" header="Name" sortable />
  </DataTable>
</template>
```

## Styling

```vue
<style scoped>
/* ✅ Используй scoped -->
.container {
  padding: 1rem;
}

/* ✅ :deep() для дочерних компонентов */
:deep(.p-datatable) {
  border: 1px solid #ccc;
}

/* ✅ :slotted() для слотов */
:slotted(.header) {
  font-weight: bold;
}
</style>
```

## Composition Functions

```vue
<script setup>
// ✅ Выноси сложную логику в composables
import { useCustomers } from '@/composables/useCustomers.js';

const {
  customers,
  loading,
  loadCustomers,
  totalRecords
} = useCustomers();

onMounted(() => {
  loadCustomers();
});
</script>
```

## Error Handling

```vue
<script setup>
import { useToast } from 'primevue';

const toast = useToast();

async function loadData() {
  try {
    const result = await apiGet('endpoint');
    if (result.success) {
      data.value = result.data;
    } else {
      toast.add({
        severity: 'error',
        summary: 'Ошибка',
        detail: result.error
      });
    }
  } catch (error) {
    console.error('Error:', error);
    toast.add({
      severity: 'error',
      summary: 'Ошибка',
      detail: 'Не удалось загрузить данные'
    });
  }
}
</script>
```

