91 lines
2.2 KiB
Vue
91 lines
2.2 KiB
Vue
<script setup lang="ts">
|
||
import { computed, ref, watch } from 'vue'
|
||
|
||
// Интерфейсы и типы
|
||
export interface Todo {
|
||
id: number
|
||
title: string
|
||
completed: boolean
|
||
priority: 'low' | 'medium' | 'high'
|
||
createdAt: Date
|
||
tags?: string[]
|
||
}
|
||
|
||
// Тип для пропсов
|
||
interface Props {
|
||
todo: Todo
|
||
showPriority?: boolean
|
||
index?: number
|
||
}
|
||
|
||
// Тип для событий
|
||
interface Emits {
|
||
(e: 'update', todo: Todo): void
|
||
(e: 'delete', id: number): void
|
||
(e: 'click', index: number): void
|
||
}
|
||
|
||
// Определение пропсов с типами
|
||
const props = withDefaults(defineProps<Props>(), {
|
||
showPriority: true,
|
||
index: 0
|
||
})
|
||
|
||
// Определение событий с типами
|
||
const emit = defineEmits<Emits>()
|
||
|
||
// Реактивные данные с типами
|
||
// const isHovered = ref<boolean>(false)
|
||
const clickCount = ref<number>(0)
|
||
|
||
// Вычисляемые свойства с типами
|
||
const priorityClass = computed((): string => {
|
||
const priorityMap = {
|
||
low: 'priority-low',
|
||
medium: 'priority-medium',
|
||
high: 'priority-high'
|
||
}
|
||
return priorityMap[props.todo.priority]
|
||
})
|
||
|
||
// Методы с типами
|
||
const handleClick = (event: MouseEvent): void => {
|
||
clickCount.value++
|
||
emit('click', props.index)
|
||
console.log(`Клик #${clickCount.value}`, event)
|
||
}
|
||
|
||
const toggleComplete = (event: Event): void => {
|
||
const input = event.target as HTMLInputElement
|
||
const updatedTodo: Todo = {
|
||
...props.todo,
|
||
completed: input.checked
|
||
}
|
||
emit('update', updatedTodo)
|
||
}
|
||
|
||
const deleteTodo = (): void => {
|
||
emit('delete', props.todo.id)
|
||
}
|
||
|
||
// Типизированные watchers
|
||
watch(() => props.todo.completed, (newValue: boolean, oldValue: boolean) => {
|
||
console.log(`Статус изменен: ${oldValue} -> ${newValue}`)
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div class="todo-item" :class="{ completed: todo.completed }" @click="handleClick">
|
||
<input type="checkbox" :checked="todo.completed" @change="toggleComplete" />
|
||
<span>{{ todo.title }}</span>
|
||
<span class="priority" :class="priorityClass">
|
||
{{ todo.priority }}
|
||
</span>
|
||
<button @click.stop="deleteTodo">Удалить</button>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
/* стили компонента */
|
||
</style>
|