Files
marketing/src/views/reports/ReportGenerator.vue
T
2025-10-06 21:06:01 +05:00

264 lines
9.3 KiB
Vue
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.
<template>
<div class="grid">
<div class="col-12">
<div class="card">
<h5>Генерация отчёта</h5>
<p>Заполните форму для создания нового отчёта на основе собранных данных.</p>
</div>
</div>
<div class="col-12 lg:col-8">
<div class="card">
<form @submit.prevent="handleGenerate" class="p-fluid">
<div class="grid">
<div class="col-12">
<div class="field">
<label for="query">Тема исследования *</label>
<InputText id="query" v-model="formData.query" :class="{ 'p-invalid': errors.query }" placeholder="Введите тему для исследования" size="large" />
<small v-if="errors.query" class="p-error">{{ errors.query }}</small>
</div>
</div>
<div class="col-12 md:col-6">
<div class="field">
<label for="lang">Язык отчёта</label>
<Dropdown id="lang" v-model="formData.lang" :options="langOptions" optionLabel="label" optionValue="value" placeholder="Выберите язык" />
</div>
</div>
<div class="col-12 md:col-6">
<div class="field">
<label for="report_type">Тип отчёта</label>
<Dropdown id="report_type" v-model="formData.report_type" :options="reportTypeOptions" optionLabel="label" optionValue="value" placeholder="Выберите тип" />
</div>
</div>
<div class="col-12 md:col-6">
<div class="field">
<label for="depth">Глубина исследования</label>
<Slider id="depth" v-model.number="formData.depth" :min="1" :max="5" :step="1" :disabled="false" />
<small class="text-600">Текущее значение: {{ formData.depth }}</small>
</div>
</div>
<div class="col-12 md:col-6">
<div class="field">
<label for="breadth">Широта исследования</label>
<Slider id="breadth" v-model.number="formData.breadth" :min="2" :max="10" :step="1" :disabled="false" />
<small class="text-600">Текущее значение: {{ formData.breadth }}</small>
</div>
</div>
<div class="col-12">
<div class="flex gap-2">
<Button type="submit" label="Сгенерировать отчёт" icon="pi pi-file-pdf" :loading="loading" :disabled="loading" size="large" />
<Button type="button" label="Очистить форму" icon="pi pi-refresh" severity="secondary" @click="resetForm" :disabled="loading" />
</div>
</div>
</div>
</form>
</div>
</div>
<div class="col-12 lg:col-4">
<div class="card">
<h6>Информация о генерации</h6>
<div class="flex flex-column gap-3">
<div class="flex align-items-center gap-2">
<i class="pi pi-info-circle text-blue-500"></i>
<span class="text-sm">Отчёт будет создан с помощью AI-исследования</span>
</div>
<div class="flex align-items-center gap-2">
<i class="pi pi-clock text-orange-500"></i>
<span class="text-sm">Время генерации: до 5 минут</span>
</div>
<div class="flex align-items-center gap-2">
<i class="pi pi-download text-green-500"></i>
<span class="text-sm">PDF-файл будет автоматически скачан</span>
</div>
</div>
</div>
<div class="card mt-3">
<h6>Параметры исследования</h6>
<div class="flex flex-column gap-2">
<div class="flex align-items-center gap-2">
<i class="pi pi-search text-purple-500"></i>
<span class="text-sm">Глубина: детальность анализа</span>
</div>
<div class="flex align-items-center gap-2">
<i class="pi pi-globe text-cyan-500"></i>
<span class="text-sm">Широта: количество источников</span>
</div>
</div>
</div>
<div class="card mt-3">
<h6>Типы отчётов</h6>
<div class="flex flex-column gap-2">
<div class="flex align-items-center gap-2">
<i class="pi pi-file-pdf text-red-500"></i>
<span class="text-sm">Отчёт - полное исследование</span>
</div>
<div class="flex align-items-center gap-2">
<i class="pi pi-question-circle text-blue-500"></i>
<span class="text-sm">Ответ - краткий ответ на вопрос</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import ReportService from '@/service/ReportService.js';
import { useToast } from 'primevue/usetoast';
import { onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const toast = useToast();
// Реактивные данные
const loading = ref(false);
// Данные формы
const formData = reactive({
query: '',
lang: 'ru',
depth: 1,
breadth: 2,
report_type: 'report'
});
// Ошибки валидации
const errors = reactive({
query: null
});
// Опции
const langOptions = [
{ label: 'Русский', value: 'ru' },
{ label: 'English', value: 'en' }
];
const reportTypeOptions = [
{ label: 'Отчёт', value: 'report' },
{ label: 'Ответ', value: 'answer' }
];
// Методы
const validateForm = () => {
// Очистка предыдущих ошибок
Object.keys(errors).forEach((key) => (errors[key] = null));
let isValid = true;
if (!formData.query.trim()) {
errors.query = 'Тема исследования обязательна';
isValid = false;
}
return isValid;
};
const handleGenerate = async () => {
if (!validateForm()) {
toast.add({
severity: 'error',
summary: 'Ошибка валидации',
detail: 'Пожалуйста, исправьте ошибки в форме',
life: 3000
});
return;
}
loading.value = true;
try {
// Подготовка данных для API
const reportData = {
query: formData.query.trim(),
lang: formData.lang,
depth: formData.depth,
breadth: formData.breadth,
report_type: formData.report_type
};
// Генерируем и скачиваем отчёт
await ReportService.generateAndDownloadReport(reportData);
toast.add({
severity: 'success',
summary: 'Отчёт сгенерирован',
detail: 'PDF-файл с отчётом скачан',
life: 5000
});
// Очищаем форму после успешной генерации
resetForm();
} catch (error) {
console.error('Ошибка генерации отчёта:', error);
toast.add({
severity: 'error',
summary: 'Ошибка',
detail: ReportService.handleError(error),
life: 5000
});
} finally {
loading.value = false;
}
};
const resetForm = () => {
Object.assign(formData, {
query: '',
lang: 'ru',
depth: 3,
breadth: 5,
report_type: 'report'
});
Object.keys(errors).forEach((key) => (errors[key] = null));
};
// Инициализация
onMounted(() => {
// Форма уже инициализирована значениями по умолчанию
});
</script>
<style scoped>
.p-fluid .field {
margin-bottom: 1rem;
}
.card {
margin-bottom: 1rem;
}
.text-sm {
font-size: 0.875rem;
}
.flex {
display: flex;
}
.flex-column {
flex-direction: column;
}
.gap-2 {
gap: 0.5rem;
}
.gap-3 {
gap: 0.75rem;
}
.align-items-center {
align-items: center;
}
</style>