sozday-veb-prilozhenie-ai-tr/script.js

424 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// AI Trip Planner - Логика приложения
// Моковые данные для генерации маршрутов
const destinations = {
beach: {
name: 'Пляжный отдых',
locations: ['Пляж Золотой', 'Прибрежный бульвар', 'Морской порт', 'Аквапарк', 'Дайвинг-центр'],
activities: {
food: ['Рыбный ресторан', 'Кафе у моря', 'Ужин на закате'],
photo: ['Фотосессия на пляже', 'Маяк для фото', 'Закатная точка'],
history: ['Морской музей', 'Старый порт'],
art: ['Галерея маринистики'],
nightlife: ['Пляжный бар', 'Ночной клуб у моря'],
shopping: ['Рынок сувениров', 'ТЦ прибрежный'],
sports: ['Водные лыжи', 'Пляжный волейбол', 'Сёрфинг'],
relax: ['SPA-центр', 'Массаж на пляже'],
adventure: ['Прогулка на катере', 'Снорклинг'],
local: ['Рыбацкая деревня', 'Местный рынок']
}
},
active: {
name: 'Активный отдых',
locations: ['Горнолыжный курорт', 'Скалодром', 'Велотропа', 'Парк приключений', 'Река для рафтинга'],
activities: {
food: ['Горное кафе', 'Ресторан местной кухни', 'Эко-кафе'],
photo: ['Панорамная площадка', 'Водопад для фото'],
history: ['Краеведческий музей'],
art: ['Галерея современного искусства'],
nightlife: ['Бар после спорта', 'Лаунж-зона'],
shopping: ['Спортивный магазин', 'Сувениры'],
sports: ['Скалолазание', 'Рафтинг', 'Велопрогулка', 'Зиплайн', 'Лыжи'],
relax: ['Баня после спорта', 'Массаж'],
adventure: ['Прыжок с тарзанки', 'Каньонинг', 'Полёт на параплане'],
local: ['Горное село', 'Местные традиции']
}
},
culture: {
name: 'Культурный отдых',
locations: ['Исторический центр', 'Музей искусств', 'Театр оперы', 'Старый город', 'Храм'],
activities: {
food: ['Ресторан высокой кухни', 'Кафе в старом городе', 'Гастро-тур'],
photo: ['Архитектурные шедевры', 'Исторические улочки'],
history: ['Краеведческий музей', 'Экскурсия по храмам', 'Древние руины'],
art: ['Художественный музей', 'Арт-галерея', 'Мастер-класс по искусству'],
nightlife: ['Театральная премьера', 'Джаз-клуб'],
shopping: ['Антикварный магазин', 'Книжный', 'Галерея сувениров'],
sports: ['Прогулка по городу'],
relax: ['Чайная церемония', 'Медитация в саду'],
adventure: ['Квест по городу', 'Ночная экскурсия'],
local: ['Встреча с местными', 'Традиционный ужин']
}
},
nature: {
name: 'Отдых на природе',
locations: ['Национальный парк', 'Озеро', 'Лесной массив', 'Горная река', 'Заповедник'],
activities: {
food: ['Пикник на природе', 'Кафе у озера', 'Эко-ресторан'],
photo: ['Животные в заповеднике', 'Пейзажи', 'Рассвет в горах'],
history: ['Музей природы', 'Эко-тропа с информацией'],
art: ['Пленэр для рисования'],
nightlife: ['Наблюдение за звёздами', 'Костёр под звёздами'],
shopping: ['Эко-лавка', 'Местные продукты'],
sports: ['Хайкинг', 'Каякинг', 'Наблюдение за птицами', 'Рыбалка'],
relax: ['Йога на природе', 'Медитация у озера'],
adventure: ['Поход с палатками', 'Сплавление по реке'],
local: ['Встреча с егерем', 'Местные легенды']
}
}
};
const weatherTips = {
beach: '☀️ Лучшее время: май-сентябрь. Не забудь солнцезащитный крем и головной убор.',
active: '🏔 Погода переменчива в горах. Возьми ветровку и удобную обувь.',
culture: '🏛 Идеально в любое время года. Многие музеи работают до 20:00.',
nature: '🌲 Бери репелленты и удобную обувь. Весной и осенью особенно красиво.'
};
const budgetTips = {
low: '💡 Бюджет ограничен? Сосредоточься на бесплатных активностях: прогулки, пляжи, парки.',
medium: '💡 Средний бюджет позволяет добавить платные экскурсии и хорошие рестораны.',
high: '💡 С таким бюджетом можно позволить VIP-услуги и эксклюзивные впечатления.'
};
let currentTrip = null;
let selectedInterests = [];
// Инициализация
document.addEventListener('DOMContentLoaded', () => {
initInterestTags();
loadSavedTrips();
});
function initInterestTags() {
const tags = document.querySelectorAll('.interest-tag');
tags.forEach(tag => {
tag.addEventListener('click', () => {
tag.classList.toggle('selected');
const tagName = tag.dataset.tag;
if (selectedInterests.includes(tagName)) {
selectedInterests = selectedInterests.filter(i => i !== tagName);
} else {
selectedInterests.push(tagName);
}
});
});
}
function scrollToSection(id) {
document.getElementById(id).scrollIntoView({ behavior: 'smooth' });
}
function generateTrip() {
const budget = parseInt(document.getElementById('budget').value);
const days = parseInt(document.getElementById('days').value);
const restType = document.getElementById('restType').value;
if (!budget || !days) {
alert('Пожалуйста, заполните бюджет и количество дней');
return;
}
if (selectedInterests.length === 0) {
selectedInterests = ['food', 'photo', 'relax'];
}
currentTrip = createRoute(budget, days, restType, selectedInterests);
displayRoute(currentTrip);
document.getElementById('results').classList.remove('hidden');
scrollToSection('results');
}
function createRoute(budget, days, restType, interests) {
const dest = destinations[restType];
const trip = {
id: Date.now(),
date: new Date().toISOString(),
budget: budget,
days: days,
restType: restType,
interests: interests,
totalSpent: 0,
routeDays: []
};
const budgetPerDay = Math.floor(budget / days);
const locationBudget = Math.floor(budgetPerDay * 0.4);
const foodBudget = Math.floor(budgetPerDay * 0.3);
const activityBudget = Math.floor(budgetPerDay * 0.3);
for (let day = 1; day <= days; day++) {
const dayPlan = {
day: day,
title: `День ${day}: ${dest.locations[(day - 1) % dest.locations.length]}`,
activities: []
};
const morningTime = 9 + Math.floor(Math.random() * 2);
const afternoonTime = morningTime + 3;
const eveningTime = afternoonTime + 4;
const interest1 = interests[day % interests.length];
const interest2 = interests[(day + 1) % interests.length];
const interest3 = interests[(day + 2) % interests.length];
const morningActivity = dest.activities[interest1]?.[0] || 'Прогулка';
const afternoonActivity = dest.activities[interest2]?.[0] || 'Обед';
const eveningActivity = dest.activities[interest3]?.[0] || 'Отдых';
const morningCost = Math.floor(locationBudget * (0.8 + Math.random() * 0.4));
const afternoonCost = Math.floor(foodBudget * (0.8 + Math.random() * 0.4));
const eveningCost = Math.floor(activityBudget * (0.8 + Math.random() * 0.4));
dayPlan.activities.push({
time: `${morningTime}:00`,
name: morningActivity,
cost: morningCost
});
dayPlan.activities.push({
time: `${afternoonTime}:00`,
name: afternoonActivity,
cost: afternoonCost
});
dayPlan.activities.push({
time: `${eveningTime}:00`,
name: eveningActivity,
cost: eveningCost
});
trip.routeDays.push(dayPlan);
trip.totalSpent += morningCost + afternoonCost + eveningCost;
}
trip.totalSpent = Math.min(trip.totalSpent, budget);
return trip;
}
function displayRoute(trip) {
const dest = destinations[trip.restType];
document.getElementById('spentAmount').textContent = trip.totalSpent.toLocaleString();
const remaining = trip.budget - trip.totalSpent;
document.getElementById('remainingAmount').textContent = remaining.toLocaleString();
const percent = Math.round((trip.totalSpent / trip.budget) * 100);
document.getElementById('budgetFill').style.width = percent + '%';
document.getElementById('budgetPercent').textContent = percent + '% использовано';
let tips = '<p><strong>🌤 По сезону:</strong> ' + weatherTips[trip.restType] + '</p>';
tips += '<p><strong>💰 По бюджету:</strong> ' + getBudgetTip(trip.budget) + '</p>';
if (trip.interests.length > 0) {
const interestNames = trip.interests.map(i => getInterestName(i)).join(', ');
tips += '<p><strong>🎯 По интересам:</strong> Маршрут оптимизирован под: ' + interestNames + '</p>';
}
document.getElementById('aiTipsContent').innerHTML = tips;
let daysHtml = '<div class="trip-card">';
daysHtml += '<h3 style="margin-bottom:var(--kt-ai-space-4)">📅 Программа по дням</h3>';
trip.routeDays.forEach(day => {
daysHtml += '<div class="trip-day">';
daysHtml += '<h4>' + day.title + '</h4>';
day.activities.forEach(activity => {
daysHtml += '<div class="activity-row">';
daysHtml += '<span class="activity-time">' + activity.time + '</span>';
daysHtml += '<span class="activity-name">' + activity.name + '</span>';
daysHtml += '<span class="activity-cost">' + activity.cost.toLocaleString() + ' ₸</span>';
daysHtml += '</div>';
});
daysHtml += '</div>';
});
daysHtml += '</div>';
document.getElementById('routeDays').innerHTML = daysHtml;
let mapPointsHtml = '';
trip.routeDays.slice(0, 5).forEach((day, index) => {
const location = day.title.split(': ')[1] || day.title;
mapPointsHtml += '<span class="map-point">';
mapPointsHtml += '<svg class="kt-icon" aria-hidden="true"><use href="#pin"></use></svg>';
mapPointsHtml += '<span>День ' + day.day + ': ' + location + '</span>';
mapPointsHtml += '</span>';
});
document.getElementById('mapPoints').innerHTML = mapPointsHtml;
}
function getInterestName(tag) {
const names = {
food: 'Еда', photo: 'Фото', history: 'История', art: 'Искусство',
nightlife: 'Ночная жизнь', shopping: 'Шопинг', sports: 'Спорт',
relax: 'Релакс', adventure: 'Приключения', local: 'Местный колорит'
};
return names[tag] || tag;
}
function getBudgetTip(budget) {
if (budget < 200000) return budgetTips.low;
if (budget < 500000) return budgetTips.medium;
return budgetTips.high;
}
function saveTrip() {
if (!currentTrip) return;
const saved = JSON.parse(localStorage.getItem('savedTrips') || '[]');
saved.push(currentTrip);
localStorage.setItem('savedTrips', JSON.stringify(saved));
alert('Маршрут сохранён!');
loadSavedTrips();
}
function loadSavedTrips() {
const saved = JSON.parse(localStorage.getItem('savedTrips') || '[]');
const container = document.getElementById('savedTripsList');
if (saved.length === 0) {
container.innerHTML = '<p style="color:var(--kt-ai-fg-muted);text-align:center;padding:var(--kt-ai-space-6)">Пока нет сохранённых поездок</p>';
return;
}
let html = '';
saved.forEach(trip => {
const date = new Date(trip.date).toLocaleDateString('ru-RU');
const destName = destinations[trip.restType]?.name || trip.restType;
html += '<div class="trip-card">';
html += '<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:var(--kt-ai-space-2)">';
html += '<div>';
html += '<h3 style="margin:0">' + destName + '</h3>';
html += '<p style="color:var(--kt-ai-fg-muted);margin:var(--kt-ai-space-1) 0 0">' + trip.days + ' дней • ' + trip.budget.toLocaleString() + ' ₸ • ' + date + '</p>';
html += '</div>';
html += '<div class="action-buttons">';
html += '<button class="kt-ai-btn" data-variant="primary" onclick="loadTrip(' + trip.id + ')">';
html += '<svg class="kt-icon" style="margin-right:var(--kt-ai-space-1)"><use href="#eye"></use></svg>Открыть';
html += '</button>';
html += '<button class="kt-ai-btn" data-variant="ghost" onclick="deleteTrip(' + trip.id + ')">';
html += '<svg class="kt-icon" style="margin-right:var(--kt-ai-space-1)"><use href="#trash"></use></svg>Удалить';
html += '</button>';
html += '</div>';
html += '</div>';
html += '</div>';
});
container.innerHTML = html;
}
function showSavedTrips() {
scrollToSection('saved');
}
function loadTrip(id) {
const saved = JSON.parse(localStorage.getItem('savedTrips') || '[]');
const trip = saved.find(t => t.id === id);
if (trip) {
currentTrip = trip;
displayRoute(trip);
document.getElementById('results').classList.remove('hidden');
scrollToSection('results');
}
}
function deleteTrip(id) {
if (!confirm('Удалить этот маршрут?')) return;
let saved = JSON.parse(localStorage.getItem('savedTrips') || '[]');
saved = saved.filter(t => t.id !== id);
localStorage.setItem('savedTrips', JSON.stringify(saved));
loadSavedTrips();
}
function clearForm() {
document.getElementById('budget').value = '';
document.getElementById('days').value = '';
document.getElementById('restType').value = 'beach';
selectedInterests = [];
document.querySelectorAll('.interest-tag').forEach(tag => tag.classList.remove('selected'));
document.getElementById('results').classList.add('hidden');
currentTrip = null;
}
function exportToPDF() {
if (!currentTrip) return;
const printWindow = window.open('', '_blank');
const content = generateExportContent('text');
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>AI Trip Planner - Маршрут</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
h1 { color: #6366f1; }
.trip-info { background: #f3f4f6; padding: 15px; border-radius: 8px; margin-bottom: 20px; }
.day { margin-bottom: 20px; padding-left: 15px; border-left: 3px solid #6366f1; }
.activity { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #e5e7eb; }
.budget-bar { height: 10px; background: #e5e7eb; border-radius: 5px; margin: 10px 0; }
.budget-fill { height: 100%; background: linear-gradient(90deg, #6366f1, #a855f7); border-radius: 5px; }
</style>
</head>
<body>
${content}
</body>
</html>
`);
printWindow.document.close();
printWindow.print();
}
function exportToText() {
if (!currentTrip) return;
const content = generateExportContent('text');
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'ai-trip-route-' + new Date().toISOString().slice(0, 10) + '.txt';
a.click();
URL.revokeObjectURL(url);
}
function generateExportContent(format) {
const dest = destinations[currentTrip.restType];
const date = new Date(currentTrip.date).toLocaleDateString('ru-RU');
let content = 'AI TRIP PLANNER\n';
content += '='.repeat(50) + '\n\n';
content += 'Тип отдыха: ' + dest.name + '\n';
content += 'Длительность: ' + currentTrip.days + ' дней(я)\n';
content += 'Бюджет: ' + currentTrip.budget.toLocaleString() + ' ₸\n';
content += 'Дата создания: ' + date + '\n';
content += 'Интересы: ' + currentTrip.interests.map(i => getInterestName(i)).join(', ') + '\n\n';
content += 'БЮДЖЕТ\n';
content += '-'.repeat(30) + '\n';
content += 'Потрачено: ' + currentTrip.totalSpent.toLocaleString() + ' ₸\n';
content += 'Осталось: ' + (currentTrip.budget - currentTrip.totalSpent).toLocaleString() + ' ₸\n\n';
content += 'МАРШРУТ\n';
content += '-'.repeat(30) + '\n';
currentTrip.routeDays.forEach(day => {
content += '\n' + day.title + '\n';
day.activities.forEach(activity => {
content += ' ' + activity.time + ' - ' + activity.name + ' (' + activity.cost.toLocaleString() + ' ₸)\n';
});
});
return content;
}