new file: main_dc/yalarba/easySite/easySite/app/components/ObjectCard.vue

new file:   main_dc/yalarba/easySite/easySite/app/components/ObjectForm.vue
	deleted:    main_dc/yalarba/easySite/easySite/app/components/forms/ObjectForm.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/edit.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/[id]/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/create.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/index.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/objects/my-objects.vue
	deleted:    main_dc/yalarba/easySite/easySite/app/pages/objects/search.vue
	modified:   main_dc/yalarba/easySite/easySite/app/pages/profile/edit.vue
add pages for object, objectsSearch, editObject, createObject
This commit is contained in:
2025-11-07 13:09:30 +05:00
parent e8981bbd45
commit 1ba3f907ac
10 changed files with 808 additions and 1600 deletions
@@ -0,0 +1,83 @@
<!-- components/ObjectCard.vue -->
<template>
<div class="card cursor-pointer" @click="$emit('click')">
<div class="relative">
<img
:src="object.image"
:alt="object.title"
class="w-full h-48 object-cover"
>
<div class="absolute top-2 right-2">
<span class="badge badge-primary">
{{ getTypeLabel(object.type) }}
</span>
</div>
</div>
<div class="card-body">
<h3 class="text-lg font-semibold mb-2">{{ object.title }}</h3>
<p class="text-gray-600 text-sm mb-3 line-clamp-2">
{{ object.description }}
</p>
<div class="flex items-center justify-between">
<div class="flex items-center space-x-1">
<span class="text-yellow-500"></span>
<span class="text-sm font-medium">{{ object.rating }}</span>
</div>
<div class="text-right">
<div class="font-bold text-primary-600">
{{ formatPrice(object.price) }}
</div>
<div class="text-xs text-gray-500">за ночь</div>
</div>
</div>
<div class="mt-3 flex items-center text-sm text-gray-500">
<span class="mr-2">📍</span>
<span>{{ object.city }}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
interface ObjectItem {
id: number
title: string
type: string
city: string
price: number
rating: number
image: string
description: string
}
interface Props {
object: ObjectItem
}
defineProps<Props>()
defineEmits<{
click: []
}>()
const getTypeLabel = (type: string) => {
const types: Record<string, string> = {
hotel: 'Отель',
sanatorium: 'Санаторий',
guest_house: 'Гостевой дом',
tour: 'Тур',
excursion: 'Экскурсия'
}
return types[type] || type
}
const formatPrice = (price: number) => {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 0
}).format(price)
}
</script>
@@ -0,0 +1,174 @@
<!-- components/ObjectForm.vue -->
<template>
<form @submit.prevent="handleSubmit" class="space-y-6">
<!-- Основная информация -->
<div class="card">
<div class="card-header">
<h3 class="text-lg font-semibold">Основная информация</h3>
</div>
<div class="card-body space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-group">
<label class="form-label">Название объекта *</label>
<input v-model="formData.title" type="text" class="form-input" required
placeholder="Введите название">
</div>
<div class="form-group">
<label class="form-label">Тип объекта *</label>
<select v-model="formData.type" class="form-select" required>
<option value="">Выберите тип</option>
<option value="hotel">Гостиница</option>
<option value="sanatorium">Санаторий</option>
<option value="guest_house">Гостевой дом</option>
<option value="tour">Тур</option>
<option value="excursion">Экскурсия</option>
<option value="restaurant">Ресторан</option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label">Описание *</label>
<textarea v-model="formData.description" class="form-input" rows="4" required
placeholder="Подробное описание объекта"></textarea>
</div>
</div>
</div>
<!-- Местоположение -->
<div class="card">
<div class="card-header">
<h3 class="text-lg font-semibold">Местоположение</h3>
</div>
<div class="card-body space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-group">
<label class="form-label">Город *</label>
<input v-model="formData.city" type="text" class="form-input" required placeholder="Город">
</div>
<div class="form-group">
<label class="form-label">Адрес *</label>
<input v-model="formData.address" type="text" class="form-input" required
placeholder="Полный адрес">
</div>
</div>
</div>
</div>
<!-- Цены и контакты -->
<div class="card">
<div class="card-header">
<h3 class="text-lg font-semibold">Цены и контакты</h3>
</div>
<div class="card-body space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="form-group">
<label class="form-label">Цена за ночь/услугу *</label>
<input v-model="formData.price" type="number" class="form-input" required placeholder="0">
</div>
<div class="form-group">
<label class="form-label">Телефон *</label>
<input v-model="formData.contact.phone" type="tel" class="form-input" required
placeholder="+7 (XXX) XXX-XX-XX">
</div>
</div>
<div class="form-group">
<label class="form-label">Email</label>
<input v-model="formData.contact.email" type="email" class="form-input"
placeholder="email@example.com">
</div>
</div>
</div>
<!-- Удобства -->
<div class="card">
<div class="card-header">
<h3 class="text-lg font-semibold">Удобства и услуги</h3>
</div>
<div class="card-body">
<div class="grid grid-cols-2 md:grid-cols-4 gap-2">
<label v-for="amenity in availableAmenities" :key="amenity" class="flex items-center space-x-2">
<input type="checkbox" :value="amenity" v-model="formData.amenities"
class="rounded border-gray-300">
<span class="text-sm">{{ amenity }}</span>
</label>
</div>
</div>
</div>
<!-- Кнопки действий -->
<div class="flex gap-4 justify-end">
<button type="button" @click="$emit('cancel')" class="btn btn-outline" :disabled="loading">
Отмена
</button>
<button type="submit" class="btn btn-primary" :disabled="loading">
<span v-if="loading">Сохранение...</span>
<span v-else>{{ props.object ? 'Обновить' : 'Создать' }}</span>
</button>
</div>
</form>
</template>
<script setup lang="ts">
interface ObjectFormData {
title: string
type: string
description: string
city: string
address: string
price: number
images: string[]
amenities: string[]
contact: {
phone: string
email: string
}
}
interface Props {
object?: ObjectFormData | null
loading?: boolean
}
const props = withDefaults(defineProps<Props>(), {
object: null,
loading: false
})
const emit = defineEmits<{
submit: [formData: ObjectFormData]
cancel: []
}>()
const availableAmenities = [
'Wi-Fi', 'Парковка', 'Бассейн', 'СПА', 'Завтрак',
'Кондиционер', 'Трансфер', 'Экскурсии', 'Баня', 'Ресторан'
]
const formData = reactive<ObjectFormData>({
title: '',
type: '',
description: '',
city: '',
address: '',
price: 0,
images: [],
amenities: [],
contact: {
phone: '',
email: ''
}
})
// Заполнение формы данными при редактировании
watch(() => props.object, (newObject) => {
if (newObject) {
Object.assign(formData, newObject)
}
}, { immediate: true })
const handleSubmit = () => {
emit('submit', { ...formData })
}
</script>
@@ -1,234 +0,0 @@
<template>
<form @submit.prevent="handleSubmit" class="space-y-8">
<!-- Основная информация -->
<div class="card">
<div class="card-header">
<h2 class="text-xl font-semibold">Основная информация</h2>
</div>
<div class="card-body space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="form-group">
<label class="form-label">Название объекта *</label>
<input v-model="form.title" type="text" class="form-input"
placeholder="Например: Отель 'Морской бриз'" required>
</div>
<div class="form-group">
<label class="form-label">Тип объекта *</label>
<select v-model="form.type" class="form-select" required>
<option value="">Выберите тип</option>
<option value="hotel">Отель</option>
<option value="apartment">Апартаменты</option>
<option value="villa">Вилла</option>
<option value="camping">Кемпинг</option>
<option value="restaurant">Ресторан</option>
<option value="attraction">Достопримечательность</option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label">Описание *</label>
<textarea v-model="form.description" rows="4" class="form-input"
placeholder="Подробное описание объекта, его преимуществ и особенностей..." required></textarea>
</div>
</div>
</div>
<!-- Местоположение -->
<div class="card">
<div class="card-header">
<h2 class="text-xl font-semibold">Местоположение</h2>
</div>
<div class="card-body space-y-6">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="form-group">
<label class="form-label">Страна *</label>
<input v-model="form.country" type="text" class="form-input" placeholder="Россия" required>
</div>
<div class="form-group">
<label class="form-label">Город *</label>
<input v-model="form.city" type="text" class="form-input" placeholder="Москва" required>
</div>
<div class="form-group">
<label class="form-label">Категория *</label>
<select v-model="form.category" class="form-select" required>
<option value="">Выберите категорию</option>
<option value="accommodation">Проживание</option>
<option value="food">Питание</option>
<option value="entertainment">Развлечения</option>
<option value="culture">Культура</option>
<option value="nature">Природа</option>
<option value="shopping">Шоппинг</option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label">Адрес *</label>
<input v-model="form.address" type="text" class="form-input" placeholder="ул. Примерная, 123"
required>
</div>
</div>
</div>
<!-- Цены и услуги -->
<div class="card">
<div class="card-header">
<h2 class="text-xl font-semibold">Цены и услуги</h2>
</div>
<div class="card-body space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="form-group">
<label class="form-label">Цена *</label>
<input v-model="form.price" type="number" class="form-input" placeholder="5000" min="0"
required>
</div>
<div class="form-group">
<label class="form-label">Единица измерения цены *</label>
<select v-model="form.priceUnit" class="form-select" required>
<option value="">Выберите единицу</option>
<option value="per_night">За ночь</option>
<option value="per_person">За человека</option>
<option value="fixed">Фиксированная цена</option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label">Услуги и удобства</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mt-2">
<label v-for="amenity in availableAmenities" :key="amenity.value"
class="flex items-center space-x-2 cursor-pointer">
<input type="checkbox" :value="amenity.value" v-model="form.amenities"
class="rounded border-gray-300">
<span class="text-sm">{{ amenity.label }}</span>
</label>
</div>
</div>
</div>
</div>
<!-- Контактная информация -->
<div class="card">
<div class="card-header">
<h2 class="text-xl font-semibold">Контактная информация</h2>
</div>
<div class="card-body space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="form-group">
<label class="form-label">Email для связи *</label>
<input v-model="form.contactEmail" type="email" class="form-input"
placeholder="contact@example.com" required>
</div>
<div class="form-group">
<label class="form-label">Телефон для связи *</label>
<input v-model="form.contactPhone" type="tel" class="form-input" placeholder="+7 999 123-45-67"
required>
</div>
</div>
<div class="form-group">
<label class="form-label">Веб-сайт (необязательно)</label>
<input v-model="form.website" type="url" class="form-input" placeholder="https://example.com">
</div>
</div>
</div>
<!-- Кнопки действий -->
<div class="flex justify-end gap-4 pt-6">
<button type="button" @click="$emit('cancel')" class="btn btn-outline" :disabled="props.loading">
Отмена
</button>
<button type="submit" class="btn btn-primary" :disabled="props.loading">
<span v-if="props.loading">Сохранение...</span>
<span v-else>{{ props.object ? 'Обновить' : 'Создать' }} объект</span>
</button>
</div>
</form>
</template>
<script setup lang="ts">
import type { ObjectItem } from '~/composables/useObjects'
interface Props {
object?: ObjectItem | null
loading?: boolean
}
interface Emits {
(e: 'submit', data: any): void
(e: 'cancel'): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
// Доступные услуги
const availableAmenities = [
{ value: 'wifi', label: 'Wi-Fi' },
{ value: 'parking', label: 'Парковка' },
{ value: 'breakfast', label: 'Завтрак' },
{ value: 'pool', label: 'Бассейн' },
{ value: 'spa', label: 'СПА' },
{ value: 'gym', label: 'Тренажерный зал' },
{ value: 'air_conditioning', label: 'Кондиционер' },
{ value: 'heating', label: 'Отопление' },
{ value: 'kitchen', label: 'Кухня' },
{ value: 'washing_machine', label: 'Стиральная машина' },
{ value: 'tv', label: 'Телевизор' },
{ value: 'elevator', label: 'Лифт' }
]
// Форма данных
const form = ref({
title: '',
description: '',
type: '',
category: '',
address: '',
city: '',
country: '',
price: 0,
priceUnit: '',
amenities: [] as string[],
contactEmail: '',
contactPhone: '',
website: ''
})
// Заполняем форму данными при редактировании
watch(() => props.object, (object) => {
if (object) {
form.value = {
title: object.title,
description: object.description,
type: object.type,
category: object.category,
address: object.address,
city: object.city,
country: object.country,
price: object.price,
priceUnit: object.priceUnit,
amenities: object.amenities || [],
contactEmail: object.contactEmail,
contactPhone: object.contactPhone,
website: object.website || ''
}
}
}, { immediate: true })
const handleSubmit = () => {
// Валидация
if (!form.value.title || !form.value.type || !form.value.description) {
alert('Пожалуйста, заполните все обязательные поля')
return
}
emit('submit', form.value)
}
</script>
@@ -1,3 +1,4 @@
<!-- pages/objects/[id]/edit.vue -->
<template> <template>
<div class="min-h-screen bg-gray-50 py-8"> <div class="min-h-screen bg-gray-50 py-8">
<div class="container max-w-4xl"> <div class="container max-w-4xl">
@@ -40,44 +41,79 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// definePageMeta({ interface ObjectData {
// middleware: 'auth' id: number
// }) title: string
type: string
description: string
city: string
address: string
price: number
images: string[]
amenities: string[]
contact: {
phone: string
email: string
}
}
// const route = useRoute() const route = useRoute()
// const { fetchObjectById, updateObject } = useObjects() const object = ref<ObjectData | null>(null)
// const object = ref(null) const loading = ref(true)
// const loading = ref(true) const updating = ref(false)
// const updating = ref(false)
// onMounted(async () => { // Мок-данные объекта
// const objectId = parseInt(route.params.id as string) const mockObject: ObjectData = {
// if (objectId) { id: 1,
// const result = await fetchObjectById(objectId) title: 'Гостевой дом "У озера"',
// object.value = result type: 'guest_house',
// } description: 'Уютный гостевой дом на берегу живописного озера в Карелии',
// loading.value = false city: 'Карелия',
// }) address: 'ул. Озерная, 15',
price: 2800,
images: [
'/images/objects/lake-house-1.jpg',
'/images/objects/lake-house-2.jpg'
],
amenities: ['Wi-Fi', 'Парковка', 'Завтрак', 'Баня'],
contact: {
phone: '+7 (911) 123-45-67',
email: 'lakehouse@example.com'
}
}
// const handleSubmit = async (formData: any) => { onMounted(async () => {
// updating.value = true // Имитация загрузки данных
await new Promise(resolve => setTimeout(resolve, 800))
object.value = mockObject
loading.value = false
})
// try { // eslint-disable-next-line @typescript-eslint/no-explicit-any
// const objectId = parseInt(route.params.id as string) const handleSubmit = async (formData: any) => {
// await updateObject(objectId, formData) updating.value = true
// alert('Объект успешно обновлен!') try {
// await navigateTo(`/objects/${objectId}`) // Имитация обновления
// } catch (error) { await new Promise(resolve => setTimeout(resolve, 1000))
// console.error('Error updating object:', error)
// alert('Ошибка при обновлении объекта')
// } finally {
// updating.value = false
// }
// }
// const handleCancel = () => { console.log('Обновление объекта:', {
// const objectId = route.params.id id: parseInt(route.params.id as string),
// navigateTo(`/objects/${objectId}`) ...formData
// } })
alert('Объект успешно обновлен!')
await navigateTo(`/objects/${route.params.id}`)
} catch (error) {
console.error('Error updating object:', error)
alert('Ошибка при обновлении объекта')
} finally {
updating.value = false
}
}
const handleCancel = () => {
const objectId = route.params.id
navigateTo(`/objects/${objectId}`)
}
</script> </script>
File diff suppressed because it is too large Load Diff
@@ -1,3 +1,4 @@
<!-- pages/objects/create.vue -->
<template> <template>
<div class="min-h-screen bg-gray-50 py-8"> <div class="min-h-screen bg-gray-50 py-8">
<div class="container max-w-4xl"> <div class="container max-w-4xl">
@@ -6,47 +7,42 @@
<p class="text-gray-600 mt-2">Заполните информацию о вашем объекте</p> <p class="text-gray-600 mt-2">Заполните информацию о вашем объекте</p>
</div> </div>
<ObjectForm <ObjectForm :loading="loading" @submit="handleSubmit" @cancel="handleCancel" />
:loading="loading"
@submit="handleSubmit"
@cancel="handleCancel"
/>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// definePageMeta({ const loading = ref(false)
// middleware: 'auth'
// })
// const { createObject } = useObjects() // eslint-disable-next-line @typescript-eslint/no-explicit-any
// const loading = ref(false) const handleSubmit = async (formData: any) => {
loading.value = true
// const handleSubmit = async (formData: any) => { try {
// loading.value = true // Имитация создания объекта
await new Promise(resolve => setTimeout(resolve, 1000))
// try { console.log('Создание объекта:', {
// await createObject({ ...formData,
// ...formData, userId: 1,
// userId: 1, // В реальном приложении из авторизации isActive: true,
// isActive: true, images: formData.images || ['/images/placeholder.jpg'],
// images: formData.images || ['/images/placeholder.jpg'], amenities: formData.amenities || []
// amenities: formData.amenities || [] })
// })
// // Показываем уведомление об успехе // Показываем уведомление об успехе
// alert('Объект успешно создан!') alert('Объект успешно создан!')
// await navigateTo('/objects/my-objects') await navigateTo('/objects/my-objects')
// } catch (error) { } catch (error) {
// console.error('Error creating object:', error) console.error('Error creating object:', error)
// alert('Ошибка при создании объекта') alert('Ошибка при создании объекта')
// } finally { } finally {
// loading.value = false loading.value = false
// } }
// } }
// const handleCancel = () => { const handleCancel = () => {
// navigateTo('/objects/my-objects') navigateTo('/objects/my-objects')
// } }
</script> </script>
@@ -1,3 +1,4 @@
<!-- pages/objects/index.vue -->
<template> <template>
<div class="min-h-screen bg-gray-50 py-8"> <div class="min-h-screen bg-gray-50 py-8">
<div class="container"> <div class="container">
@@ -12,12 +13,42 @@
</NuxtLink> </NuxtLink>
</div> </div>
<!-- Простой поиск --> <!-- Фильтры поиска -->
<div class="search-filters mb-8"> <div class="search-filters mb-8">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
<div class="form-group">
<label class="form-label">Тип объекта</label>
<select v-model="filters.type" class="form-select">
<option value="">Все типы</option>
<option value="hotel">Гостиница</option>
<option value="sanatorium">Санаторий</option>
<option value="tour">Тур</option>
<option value="restaurant">Ресторан</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Город</label>
<input
v-model="filters.city"
type="text"
class="form-input"
placeholder="Введите город"
>
</div>
<div class="form-group">
<label class="form-label">Цена до</label>
<input
v-model="filters.maxPrice"
type="number"
class="form-input"
placeholder="Макс. цена"
>
</div>
</div>
<div class="flex gap-4"> <div class="flex gap-4">
<div class="form-group flex-1"> <div class="form-group flex-1">
<input <input
v-model="searchQuery" v-model="filters.search"
type="text" type="text"
class="form-input" class="form-input"
placeholder="Поиск по названию, городу или описанию..." placeholder="Поиск по названию, городу или описанию..."
@@ -71,31 +102,107 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ObjectItem } from '~/composables/useObjects' interface ObjectItem {
id: number
title: string
type: string
city: string
price: number
rating: number
image: string
description: string
isActive: boolean
}
const route = useRoute() // Мок-данные
const { objects, loading, fetchObjects } = useObjects() const mockObjects: ObjectItem[] = [
{
const searchQuery = ref(route.query.search as string || '') id: 1,
title: 'Горный отель "Эдельвейс"',
// Загрузка объектов при монтировании type: 'hotel',
onMounted(() => { city: 'Сочи',
if (searchQuery.value) { price: 4500,
searchObjects() rating: 4.8,
} else { image: '/images/hotels/edelweiss.jpg',
fetchObjects() description: 'Комфортабельный отель в горах с видом на море',
isActive: true
},
{
id: 2,
title: 'Тур по Алтаю',
type: 'tour',
city: 'Горно-Алтайск',
price: 12500,
rating: 4.9,
image: '/images/tours/altai.jpg',
description: '7-дневный тур по самым живописным местам Алтая',
isActive: true
},
{
id: 3,
title: 'Санаторий "Здоровье"',
type: 'sanatorium',
city: 'Кисловодск',
price: 3200,
rating: 4.6,
image: '/images/sanatoriums/health.jpg',
description: 'Лечебно-оздоровительный комплекс с минеральными водами',
isActive: true
} }
]
const objects = ref<ObjectItem[]>(mockObjects)
const loading = ref(false)
const filters = ref({
search: '',
type: '',
city: '',
maxPrice: null as number | null
}) })
const searchObjects = async () => { const searchObjects = async () => {
await fetchObjects({ loading.value = true
search: searchQuery.value // Имитация загрузки
}) await new Promise(resolve => setTimeout(resolve, 500))
let filtered = [...mockObjects]
if (filters.value.search) {
const search = filters.value.search.toLowerCase()
filtered = filtered.filter(obj =>
obj.title.toLowerCase().includes(search) ||
obj.city.toLowerCase().includes(search) ||
obj.description.toLowerCase().includes(search)
)
}
if (filters.value.type) {
filtered = filtered.filter(obj => obj.type === filters.value.type)
}
if (filters.value.city) {
filtered = filtered.filter(obj =>
obj.city.toLowerCase().includes(filters.value.city.toLowerCase())
)
}
if (filters.value.maxPrice) {
filtered = filtered.filter(obj => obj.price <= filters.value.maxPrice!)
}
objects.value = filtered
loading.value = false
} }
const resetSearch = async () => { const resetSearch = async () => {
searchQuery.value = '' filters.value = {
await fetchObjects() search: '',
type: '',
city: '',
maxPrice: null
}
objects.value = mockObjects
} }
const navigateToObject = (id: number) => { const navigateToObject = (id: number) => {
@@ -1,3 +1,4 @@
<!-- pages/objects/my-objects.vue -->
<template> <template>
<div class="min-h-screen bg-gray-50 py-8"> <div class="min-h-screen bg-gray-50 py-8">
<div class="container"> <div class="container">
@@ -17,12 +18,47 @@
</NuxtLink> </NuxtLink>
</div> </div>
</div> </div>
</div>
<!-- Информация --> <!-- Фильтры -->
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6"> <div class="card mb-6">
<p class="text-blue-700"> <div class="card-body">
💡 Это демо-режим. В реальном приложении здесь будут отображаться только ваши объекты. <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
</p> <div class="form-group">
<label class="form-label">Статус</label>
<select v-model="filters.status" class="form-select">
<option value="">Все статусы</option>
<option value="active">Активные</option>
<option value="inactive">Неактивные</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Тип</label>
<select v-model="filters.type" class="form-select">
<option value="">Все типы</option>
<option value="hotel">Гостиница</option>
<option value="sanatorium">Санаторий</option>
<option value="tour">Тур</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Поиск</label>
<input
v-model="filters.search"
type="text"
class="form-input"
placeholder="Название или город"
>
</div>
</div>
<div class="flex gap-2 mt-4">
<button class="btn btn-primary" @click="applyFilters">
Применить
</button>
<button class="btn btn-outline" @click="resetFilters">
Сбросить
</button>
</div>
</div> </div>
</div> </div>
@@ -126,15 +162,116 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// import { ObjectItem } from '~/composables/useObjects' interface MyObjectItem {
id: number
title: string
type: string
city: string
price: number
isActive: boolean
createdAt: string
}
// // Убрали middleware - страница доступна всем // Мок-данные
const mockMyObjects: MyObjectItem[] = [
{
id: 1,
title: 'Гостевой дом "У озера"',
type: 'guest_house',
city: 'Карелия',
price: 2800,
isActive: true,
createdAt: '2024-01-15'
},
{
id: 2,
title: 'Экскурсия по историческому центру',
type: 'excursion',
city: 'Санкт-Петербург',
price: 1500,
isActive: true,
createdAt: '2024-01-10'
},
{
id: 3,
title: 'Горнолыжный курорт "Снежный"',
type: 'hotel',
city: 'Красная Поляна',
price: 5200,
isActive: false,
createdAt: '2024-01-05'
}
]
// const { fetchMyObjects, deleteObject, objects: myObjects, loading } = useObjects() const myObjects = ref<MyObjectItem[]>(mockMyObjects)
const loading = ref(false)
// onMounted(async () => { const filters = ref({
// await fetchMyObjects() status: '',
// }) type: '',
search: ''
})
// ... остальные функции без изменений ... const applyFilters = () => {
let filtered = [...mockMyObjects]
if (filters.value.status) {
filtered = filtered.filter(obj =>
filters.value.status === 'active' ? obj.isActive : !obj.isActive
)
}
if (filters.value.type) {
filtered = filtered.filter(obj => obj.type === filters.value.type)
}
if (filters.value.search) {
const search = filters.value.search.toLowerCase()
filtered = filtered.filter(obj =>
obj.title.toLowerCase().includes(search) ||
obj.city.toLowerCase().includes(search)
)
}
myObjects.value = filtered
}
const resetFilters = () => {
filters.value = {
status: '',
type: '',
search: ''
}
myObjects.value = mockMyObjects
}
const deleteObject = async (id: number) => {
if (confirm('Вы уверены, что хотите удалить этот объект?')) {
// Имитация удаления
myObjects.value = myObjects.value.filter(obj => obj.id !== id)
}
}
const getTypeLabel = (type: string) => {
const types: Record<string, string> = {
hotel: 'Гостиница',
sanatorium: 'Санаторий',
guest_house: 'Гостевой дом',
tour: 'Тур',
excursion: 'Экскурсия'
}
return types[type] || type
}
const formatPrice = (price: number) => {
return new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
minimumFractionDigits: 0
}).format(price)
}
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('ru-RU')
}
</script> </script>
@@ -1,6 +0,0 @@
<template>
<div>
<h1>Страница</h1>
<p>Содержимое страницы</p>
</div>
</template>
@@ -298,6 +298,7 @@ const verifyEmailCode = async () => {
verificationError.value = 'Неверный код подтверждения'; verificationError.value = 'Неверный код подтверждения';
} }
} catch (error) { } catch (error) {
console.log("debug. Error: " + error)
verificationError.value = 'Ошибка при проверке кода. Попробуйте позже.'; verificationError.value = 'Ошибка при проверке кода. Попробуйте позже.';
} finally { } finally {
isVerifyingCode.value = false; isVerifyingCode.value = false;