This commit is contained in:
root
2025-10-06 21:06:01 +05:00
parent b887e4d652
commit d808b9acff
5 changed files with 384 additions and 223 deletions
+66 -125
View File
@@ -13,62 +13,39 @@
<div class="grid">
<div class="col-12">
<div class="field">
<label for="reportTitle">Заголовок отчёта *</label>
<InputText id="reportTitle" v-model="formData.reportTitle" :class="{ 'p-invalid': errors.reportTitle }" placeholder="Введите заголовок отчёта" size="large" />
<small v-if="errors.reportTitle" class="p-error">{{ errors.reportTitle }}</small>
<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="authorName">Автор *</label>
<InputText id="authorName" v-model="formData.authorName" :class="{ 'p-invalid': errors.authorName }" placeholder="Введите имя автора" />
<small v-if="errors.authorName" class="p-error">{{ errors.authorName }}</small>
<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="companyName">Компания *</label>
<InputText id="companyName" v-model="formData.companyName" :class="{ 'p-invalid': errors.companyName }" placeholder="Введите название компании" />
<small v-if="errors.companyName" class="p-error">{{ errors.companyName }}</small>
<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="startDate">Дата начала *</label>
<Calendar id="startDate" v-model="formData.startDate" :class="{ 'p-invalid': errors.startDate }" dateFormat="dd.mm.yy" showTime hourFormat="24" placeholder="Выберите дату начала" :maxDate="formData.endDate" />
<small v-if="errors.startDate" class="p-error">{{ errors.startDate }}</small>
<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="endDate">Дата окончания *</label>
<Calendar id="endDate" v-model="formData.endDate" :class="{ 'p-invalid': errors.endDate }" dateFormat="dd.mm.yy" showTime hourFormat="24" placeholder="Выберите дату окончания" :minDate="formData.startDate" />
<small v-if="errors.endDate" class="p-error">{{ errors.endDate }}</small>
</div>
</div>
<div class="col-12 md:col-6">
<div class="field">
<label for="format">Формат файла *</label>
<Dropdown id="format" v-model="formData.format" :options="formatOptions" optionLabel="label" optionValue="value" placeholder="Выберите формат" />
</div>
</div>
<div class="col-12 md:col-6">
<div class="field">
<label for="template">Шаблон отчёта</label>
<Dropdown id="template" v-model="formData.template" :options="templateOptions" optionLabel="label" optionValue="value" placeholder="Выберите шаблон" />
</div>
</div>
<div class="col-12">
<div class="field">
<label for="description">Описание отчёта</label>
<Textarea id="description" v-model="formData.description" rows="3" placeholder="Введите краткое описание отчёта (необязательно)" />
<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>
@@ -89,29 +66,43 @@
<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">Отчёт будет создан на основе данных за выбранный период</span>
<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">Время генерации: 1-3 минуты</span>
<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">Файл будет автоматически скачан</span>
<span class="text-sm">PDF-файл будет автоматически скачан</span>
</div>
</div>
</div>
<div class="card mt-3">
<h6>Поддерживаемые форматы</h6>
<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">PDF - для печати и просмотра</span>
<span class="text-sm">Отчёт - полное исследование</span>
</div>
<div class="flex align-items-center gap-2">
<i class="pi pi-file-word text-blue-500"></i>
<span class="text-sm">DOCX - для редактирования</span>
<i class="pi pi-question-circle text-blue-500"></i>
<span class="text-sm">Ответ - краткий ответ на вопрос</span>
</div>
</div>
</div>
@@ -133,35 +124,27 @@ const loading = ref(false);
// Данные формы
const formData = reactive({
reportTitle: '',
authorName: '',
companyName: '',
startDate: null,
endDate: null,
format: 'PDF',
template: 'standard',
description: ''
query: '',
lang: 'ru',
depth: 1,
breadth: 2,
report_type: 'report'
});
// Ошибки валидации
const errors = reactive({
reportTitle: null,
authorName: null,
companyName: null,
startDate: null,
endDate: null
query: null
});
// Опции
const formatOptions = [
{ label: 'PDF', value: 'PDF' },
{ label: 'DOCX', value: 'DOCX' }
const langOptions = [
{ label: 'Русский', value: 'ru' },
{ label: 'English', value: 'en' }
];
const templateOptions = [
{ label: 'Стандартный', value: 'standard' },
{ label: 'Краткий', value: 'brief' },
{ label: 'Подробный', value: 'detailed' }
const reportTypeOptions = [
{ label: 'Отчёт', value: 'report' },
{ label: 'Ответ', value: 'answer' }
];
// Методы
@@ -171,33 +154,8 @@ const validateForm = () => {
let isValid = true;
if (!formData.reportTitle.trim()) {
errors.reportTitle = 'Заголовок отчёта обязателен';
isValid = false;
}
if (!formData.authorName.trim()) {
errors.authorName = 'Имя автора обязательно';
isValid = false;
}
if (!formData.companyName.trim()) {
errors.companyName = 'Название компании обязательно';
isValid = false;
}
if (!formData.startDate) {
errors.startDate = 'Дата начала обязательна';
isValid = false;
}
if (!formData.endDate) {
errors.endDate = 'Дата окончания обязательна';
isValid = false;
}
if (formData.startDate && formData.endDate && formData.startDate >= formData.endDate) {
errors.endDate = 'Дата окончания должна быть позже даты начала';
if (!formData.query.trim()) {
errors.query = 'Тема исследования обязательна';
isValid = false;
}
@@ -220,34 +178,25 @@ const handleGenerate = async () => {
try {
// Подготовка данных для API
const reportData = {
reportTitle: formData.reportTitle.trim(),
authorName: formData.authorName.trim(),
companyName: formData.companyName.trim(),
startDate: formData.startDate.toISOString(),
endDate: formData.endDate.toISOString(),
format: formData.format
query: formData.query.trim(),
lang: formData.lang,
depth: formData.depth,
breadth: formData.breadth,
report_type: formData.report_type
};
const blob = await ReportService.generateReport(reportData);
// Создание имени файла
const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
const filename = `report_${timestamp}.${formData.format.toLowerCase()}`;
// Скачивание файла
ReportService.downloadFile(blob, filename);
// Генерируем и скачиваем отчёт
await ReportService.generateAndDownloadReport(reportData);
toast.add({
severity: 'success',
summary: 'Успешно',
detail: 'Отчёт сгенерирован и скачан',
life: 3000
summary: 'Отчёт сгенерирован',
detail: 'PDF-файл с отчётом скачан',
life: 5000
});
// Переход к истории отчётов
setTimeout(() => {
router.push('/reports/history');
}, 2000);
// Очищаем форму после успешной генерации
resetForm();
} catch (error) {
console.error('Ошибка генерации отчёта:', error);
toast.add({
@@ -263,14 +212,11 @@ const handleGenerate = async () => {
const resetForm = () => {
Object.assign(formData, {
reportTitle: '',
authorName: '',
companyName: '',
startDate: null,
endDate: null,
format: 'PDF',
template: 'standard',
description: ''
query: '',
lang: 'ru',
depth: 3,
breadth: 5,
report_type: 'report'
});
Object.keys(errors).forEach((key) => (errors[key] = null));
@@ -278,12 +224,7 @@ const resetForm = () => {
// Инициализация
onMounted(() => {
// Установка дат по умолчанию (последние 7 дней)
const today = new Date();
const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
formData.endDate = today;
formData.startDate = weekAgo;
// Форма уже инициализирована значениями по умолчанию
});
</script>
+65 -85
View File
@@ -3,7 +3,7 @@
<div class="col-12">
<div class="card">
<h5>Генерация отчётов</h5>
<p>Создайте новый отчёт на основе собранных данных за выбранный период.</p>
<p>Создайте новый отчёт с помощью AI-исследования по выбранной теме.</p>
</div>
</div>
@@ -13,38 +13,29 @@
<h5>Создать новый отчёт</h5>
<form @submit.prevent="handleGenerate" class="p-fluid">
<div class="field">
<label for="reportTitle">Заголовок отчёта *</label>
<InputText id="reportTitle" v-model="formData.reportTitle" :class="{ 'p-invalid': errors.reportTitle }" placeholder="Введите заголовок отчёта" />
<small v-if="errors.reportTitle" class="p-error">{{ errors.reportTitle }}</small>
<label for="query">Тема исследования *</label>
<InputText id="query" v-model="formData.query" :class="{ 'p-invalid': errors.query }" placeholder="Введите тему для исследования" />
<small v-if="errors.query" class="p-error">{{ errors.query }}</small>
</div>
<div class="field">
<label for="authorName">Автор *</label>
<InputText id="authorName" v-model="formData.authorName" :class="{ 'p-invalid': errors.authorName }" placeholder="Введите имя автора" />
<small v-if="errors.authorName" class="p-error">{{ errors.authorName }}</small>
<label for="lang">Язык отчёта</label>
<Dropdown id="lang" v-model="formData.lang" :options="langOptions" optionLabel="label" optionValue="value" placeholder="Выберите язык" />
</div>
<div class="field">
<label for="companyName">Компания *</label>
<InputText id="companyName" v-model="formData.companyName" :class="{ 'p-invalid': errors.companyName }" placeholder="Введите название компании" />
<small v-if="errors.companyName" class="p-error">{{ errors.companyName }}</small>
<label for="report_type">Тип отчёта</label>
<Dropdown id="report_type" v-model="formData.report_type" :options="reportTypeOptions" optionLabel="label" optionValue="value" placeholder="Выберите тип" />
</div>
<div class="field">
<label for="startDate">Дата начала *</label>
<Calendar id="startDate" v-model="formData.startDate" :class="{ 'p-invalid': errors.startDate }" dateFormat="dd.mm.yy" showTime hourFormat="24" placeholder="Выберите дату начала" />
<small v-if="errors.startDate" class="p-error">{{ errors.startDate }}</small>
<label for="depth">Глубина исследования: {{ formData.depth }}</label>
<Slider id="depth" v-model.number="formData.depth" :min="1" :max="5" :step="1" :disabled="false" />
</div>
<div class="field">
<label for="endDate">Дата окончания *</label>
<Calendar id="endDate" v-model="formData.endDate" :class="{ 'p-invalid': errors.endDate }" dateFormat="dd.mm.yy" showTime hourFormat="24" placeholder="Выберите дату окончания" />
<small v-if="errors.endDate" class="p-error">{{ errors.endDate }}</small>
</div>
<div class="field">
<label for="format">Формат файла *</label>
<Dropdown id="format" v-model="formData.format" :options="formatOptions" optionLabel="label" optionValue="value" placeholder="Выберите формат" />
<label for="breadth">Широта исследования: {{ formData.breadth }}</label>
<Slider id="breadth" v-model.number="formData.breadth" :min="2" :max="10" :step="1" :disabled="false" />
</div>
<div class="field">
@@ -159,27 +150,27 @@ const downloadingReports = ref([]);
// Данные формы
const formData = reactive({
reportTitle: '',
authorName: '',
companyName: '',
startDate: null,
endDate: null,
format: 'PDF'
query: '',
lang: 'ru',
depth: 1,
breadth: 2,
report_type: 'report'
});
// Ошибки валидации
const errors = reactive({
reportTitle: null,
authorName: null,
companyName: null,
startDate: null,
endDate: null
query: null
});
// Опции форматов
const formatOptions = [
{ label: 'PDF', value: 'PDF' },
{ label: 'DOCX', value: 'DOCX' }
// Опции
const langOptions = [
{ label: 'Русский', value: 'ru' },
{ label: 'English', value: 'en' }
];
const reportTypeOptions = [
{ label: 'Отчёт', value: 'report' },
{ label: 'Ответ', value: 'answer' }
];
// Вычисляемые свойства
@@ -202,33 +193,8 @@ const validateForm = () => {
let isValid = true;
if (!formData.reportTitle.trim()) {
errors.reportTitle = 'Заголовок отчёта обязателен';
isValid = false;
}
if (!formData.authorName.trim()) {
errors.authorName = 'Имя автора обязательно';
isValid = false;
}
if (!formData.companyName.trim()) {
errors.companyName = 'Название компании обязательно';
isValid = false;
}
if (!formData.startDate) {
errors.startDate = 'Дата начала обязательна';
isValid = false;
}
if (!formData.endDate) {
errors.endDate = 'Дата окончания обязательна';
isValid = false;
}
if (formData.startDate && formData.endDate && formData.startDate >= formData.endDate) {
errors.endDate = 'Дата окончания должна быть позже даты начала';
if (!formData.query.trim()) {
errors.query = 'Тема исследования обязательна';
isValid = false;
}
@@ -251,32 +217,34 @@ const handleGenerate = async () => {
try {
// Подготовка данных для API
const reportData = {
reportTitle: formData.reportTitle.trim(),
authorName: formData.authorName.trim(),
companyName: formData.companyName.trim(),
startDate: formData.startDate.toISOString(),
endDate: formData.endDate.toISOString(),
format: formData.format
query: formData.query.trim(),
lang: formData.lang,
depth: formData.depth,
breadth: formData.breadth,
report_type: formData.report_type
};
const blob = await ReportService.generateReport(reportData);
// Генерируем и скачиваем отчёт (или показываем сообщение)
const result = await ReportService.generateAndDownloadReport(reportData);
// Создание имени файла
const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
const filename = `report_${timestamp}.${formData.format.toLowerCase()}`;
if (result && result.downloaded) {
toast.add({
severity: 'success',
summary: 'Отчёт сгенерирован',
detail: 'PDF-файл с отчётом скачан',
life: 5000
});
} else {
toast.add({
severity: 'info',
summary: 'Информация',
detail: result && result.message ? result.message : 'Запрос выполнен',
life: 5000
});
}
// Скачивание файла
ReportService.downloadFile(blob, filename);
toast.add({
severity: 'success',
summary: 'Успешно',
detail: 'Отчёт сгенерирован и скачан',
life: 3000
});
// Перезагрузка списка отчётов
await loadReports();
// Очищаем форму после успешной генерации
resetForm();
} catch (error) {
console.error('Ошибка генерации отчёта:', error);
toast.add({
@@ -358,6 +326,18 @@ const formatFileSize = (bytes) => {
return ReportService.formatFileSize(bytes);
};
const resetForm = () => {
Object.assign(formData, {
query: '',
lang: 'ru',
depth: 3,
breadth: 5,
report_type: 'report'
});
Object.keys(errors).forEach((key) => (errors[key] = null));
};
// Инициализация
onMounted(() => {
loadReports();