добавление выгрузки

This commit is contained in:
2026-04-07 10:41:21 +10:00
parent 30e99d3b2c
commit 4a57d68c8c
9 changed files with 199 additions and 128 deletions

View File

@@ -1,7 +1,7 @@
<script setup>
import { onMounted, ref, watch } from "vue";
import My_naw from "./components/My_naw.vue";
import Section from "./components/Section.vue";
import All_new_section from "./components/All_new_section.vue";
import General_section from "./components/General_section.vue";
import Setings from "./components/Setings.vue";
import Authe from "./components/Authe.vue";
@@ -32,23 +32,22 @@ function handleUpdate(newValue) {
<Authe :currentPage="currentPage" @update="handleUpdate" />
<div v-if="currentPage === 'rezylt'">
<div class="sm:flex">
<My_naw :currentPage="currentPage" @update="handleUpdate" />
<!-- <Section :items="items" /> -->
<Section filter="all" />
<All_new_section :currentPage="currentPage" @update="handleUpdate" />
<General_section filter="all" />
</div>
</div>
<div v-if="currentPage === 'setings'">
<div class="sm:flex">
<My_naw :currentPage="currentPage" @update="handleUpdate" />
<All_new_section :currentPage="currentPage" @update="handleUpdate" />
<Setings />
</div>
</div>
<div v-if="currentPage === 'status'">
<div class="sm:flex">
<My_naw :currentPage="currentPage" @update="handleUpdate" />
<Section filter="status" />
<All_new_section :currentPage="currentPage" @update="handleUpdate" />
<General_section filter="status" />
</div>
</div>
</template>

View File

@@ -0,0 +1,62 @@
<template>
<div class="date-input-container relative flex-grow">
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
type="date"
:placeholder="placeholder"
class="date-input dark:bg-gray-900 border-slate-100 shadow rounded-xl p-3 cursor-pointer w-full appearance-none"
/>
<span class="calendar-icon dark:text-neutral-400 pointer-events-none">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
</span>
</div>
</template>
<script setup>
defineProps({
modelValue: {
type: String,
default: "",
},
placeholder: {
type: String,
default: "",
},
});
defineEmits(["update:modelValue"]);
</script>
<style scoped>
/* Скрываем стандартную иконку календаря */
.date-input::-webkit-calendar-picker-indicator {
opacity: 0;
position: absolute;
width: 100%;
height: 100%;
cursor: pointer;
}
.calendar-icon {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
color: #999;
}
</style>

View File

@@ -64,7 +64,7 @@
<script setup>
import { ref, onMounted, onUnmounted, watch, nextTick } from "vue";
import Stat from "./Stat.vue";
import Stat from "./One_kard.vue";
import Time from "./Time.vue";
import axios from "axios";
@@ -106,7 +106,7 @@ const fetchData = async (url) => {
const fetchTotalCount = async (filterValue) => {
try {
const { data } = await axios.get(
`https://allowlgroup.ru/api/8002/records_all/count?item=${filterValue}`
`https://allowlgroup.ru/api/8002/records_all/count?item=${filterValue}`,
);
return data.count;
} catch (err) {
@@ -118,7 +118,7 @@ const fetchTotalCount = async (filterValue) => {
const fetchSearchCount = async (query, filterValue) => {
try {
const { data } = await axios.get(
`https://allowlgroup.ru/api/8002/poisk/count?query=${query}&item=${filterValue}`
`https://allowlgroup.ru/api/8002/poisk/count?query=${query}&item=${filterValue}`,
);
return data.count;
} catch (err) {
@@ -177,13 +177,13 @@ const checkForUpdates = async () => {
try {
const totalCount = await fetchTotalCount(currentFilter);
const data = await fetchData(
`https://allowlgroup.ru/api/8002/records_all?item=${currentFilter}&offset=0&limit=${LIMIT}`
`https://allowlgroup.ru/api/8002/records_all?item=${currentFilter}&offset=0&limit=${LIMIT}`,
);
if (!data.length) return;
// Создаём Map существующих URL для быстрого поиска
const existingUrls = new Map(items.value.map(item => [item.url, item]));
const existingUrls = new Map(items.value.map((item) => [item.url, item]));
const newItems = [];
let hasNew = false;
@@ -195,7 +195,10 @@ const checkForUpdates = async () => {
// Новая запись - добавляем в начало
newItems.push(item);
hasNew = true;
} else if (existing.viewed !== item.viewed || existing.status !== item.status) {
} else if (
existing.viewed !== item.viewed ||
existing.status !== item.status
) {
// Изменились viewed/status - обновляем
const index = items.value.indexOf(existing);
items.value[index] = { ...item };
@@ -207,7 +210,6 @@ const checkForUpdates = async () => {
if (newItems.length > 0) {
items.value = [...newItems, ...items.value];
}
} catch (err) {
console.error("Ошибка при проверке обновлений:", err);
}
@@ -228,12 +230,12 @@ const stopPolling = () => {
// === Обработчики событий от Stat ===
const handleViewedChange = ({ url, viewed }) => {
const item = items.value.find(i => i.url === url);
const item = items.value.find((i) => i.url === url);
if (item) item.viewed = viewed;
};
const handleStatusChange = ({ url, status }) => {
const item = items.value.find(i => i.url === url);
const item = items.value.find((i) => i.url === url);
if (item) item.status = status;
};
@@ -255,7 +257,7 @@ watch(
currentFilter = filterValue;
poisk.value = "";
loadItems(filterValue);
}
},
);
const onfilterItems = (filterValue) => {
@@ -294,7 +296,7 @@ onMounted(() => {
loadItems(currentFilter, true); // append = true
}
},
{ rootMargin: "100px" }
{ rootMargin: "100px" },
);
if (sentinel.value) {

View File

@@ -1,48 +0,0 @@
<template>
<div>
<button class="rounded-md bg-white/10 px-2.5 py-1.5 text-sm font-semibold text-white inset-ring inset-ring-white/5 hover:bg-white/20" @click="open = true">Open drawer</button>
<TransitionRoot as="template" :show="open">
<Dialog class="relative z-10" @close="open = false">
<TransitionChild as="template" enter="ease-in-out duration-500" enter-from="opacity-0" enter-to="" leave="ease-in-out duration-500" leave-from="" leave-to="opacity-0">
<div class="fixed inset-0 bg-gray-900/50 transition-opacity"></div>
</TransitionChild>
<div class="fixed inset-0 overflow-hidden">
<div class="absolute inset-0 overflow-hidden">
<div class="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10 sm:pl-16">
<TransitionChild as="template" enter="transform transition ease-in-out duration-500 sm:duration-700" enter-from="translate-x-full" enter-to="translate-x-0" leave="transform transition ease-in-out duration-500 sm:duration-700" leave-from="translate-x-0" leave-to="translate-x-full">
<DialogPanel class="pointer-events-auto relative w-screen max-w-md">
<TransitionChild as="template" enter="ease-in-out duration-500" enter-from="opacity-0" enter-to="" leave="ease-in-out duration-500" leave-from="" leave-to="opacity-0">
<div class="absolute top-0 left-0 -ml-8 flex pt-4 pr-2 sm:-ml-10 sm:pr-4">
<button type="button" class="relative rounded-md text-gray-400 hover:text-white focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-500" @click="open = false">
<span class="absolute -inset-2.5"></span>
<span class="sr-only">Close panel</span>
<XMarkIcon class="size-6" aria-hidden="true" />
</button>
</div>
</TransitionChild>
<div class="relative flex h-full flex-col overflow-y-auto bg-gray-800 py-6 shadow-xl after:absolute after:inset-y-0 after:left-0 after:w-px after:bg-white/10">
<div class="px-4 sm:px-6">
<DialogTitle class="text-base font-semibold text-white">Panel title</DialogTitle>
</div>
<div class="relative mt-6 flex-1 px-4 sm:px-6">
<!-- Your content -->
</div>
</div>
</DialogPanel>
</TransitionChild>
</div>
</div>
</div>
</Dialog>
</TransitionRoot>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Dialog, DialogPanel, DialogTitle, TransitionChild, TransitionRoot } from '@headlessui/vue'
import { XMarkIcon } from '@heroicons/vue/24/outline'
const open = ref(true)
</script>

View File

@@ -106,6 +106,7 @@ const download = async () => {
try {
const rez = await axios.get(
"https://allowlgroup.ru/api/8001/file_download",
// "http://127.0.0.1:8001/file_download",
{
params: {
path: props.article_date.split(" ")[0],

View File

@@ -3,12 +3,7 @@
<!-- Ввод времени -->
<div class="bg-white p-4 mb-4 flex-colum sm:flex dark:bg-gray-800">
<div class="w-full sm:max-w-100 flex">
<input
v-model="time"
type="text"
placeholder="01.01.2026"
class="dark:bg-gray-900 min-w-30 border-slate-100 shadow rounded-xl w-1/2 p-3"
/>
<DatePicker v-model="time" placeholder="01.01.2026" />
<button
class="dark:bg-orange-500 hover:dark:bg-orange-600 ml-4 shadow text-white bg-sky-700 w-1/2 hover:bg-sky-900 rounded-xl px-2 min-h-11 cursor-pointer"
@click="start_parser_1"
@@ -129,26 +124,32 @@
>
{{ task.finished_at }}
</td>
<td
>
<button class="text-red-500 p-1 rounded-full hover:text-red-700 cursor-pointer" @click="delete_row(task.id)">x</button>
<td>
<button
class="text-red-500 p-1 rounded-full hover:text-red-700 cursor-pointer"
@click="delete_row(task.id)"
>
x
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<Setings_downloads />
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import axios from "axios";
import Setings_downloads from "./Setings_downloads.vue";
import DatePicker from "./DatePicker.vue";
const sources = ref([
{ url: "", name: "", promt: "" },
]);
const sources = ref([{ url: "", name: "", promt: "" }]);
const time = ref(getTodayDate());
const currentSource = ref(sources.value[0]);
const promt = ref("Начальный текст");
const promt_url = ref("https://");
@@ -157,6 +158,11 @@ const tasks = ref([]);
const showLogs = ref(false); // скрыто по умолчанию
const showtask = ref(false); // скрыто по умолчанию
// Функция для получения сегодняшней даты в формате YYYY-MM-DD
function getTodayDate() {
return new Date().toISOString().split("T")[0];
}
// функция для переключения видимости логов
function toggleLogs() {
showLogs.value = !showLogs.value;
@@ -186,15 +192,14 @@ const loadTasks = async () => {
const response = await axios.get(
"https://allowlgroup.ru/api/8001/get_tasks_offset",
);
// Предположим, что ответ уже массив задач
tasks.value = response.data; // или response.data если это массив
tasks.value = response.data;
} catch (error) {
console.error("Ошибка при загрузке задач:", error);
}
};
const delete_row = async (id) => {
try{
try {
const response = await axios.delete(
`https://allowlgroup.ru/api/8001/delete_task/${id}`,
);
@@ -203,7 +208,7 @@ const delete_row = async (id) => {
} catch (error) {
console.error("Ошибка при удалении задачи:", error);
}
}
};
// Загрузка настроек при монтировании
onMounted(() => {
@@ -213,7 +218,9 @@ onMounted(() => {
// Получение настроек с сервера
const loadSettings = async () => {
try {
const response = await axios.get("https://allowlgroup.ru/api/8001/settings");
const response = await axios.get(
"https://allowlgroup.ru/api/8001/settings",
);
sources.value = response.data.sources;
currentSource.value = sources.value[0];
promt.value = currentSource.value.promt;
@@ -248,6 +255,7 @@ const saveSources = async () => {
// Запуск парсинга 1
const start_parser_1 = async () => {
try {
// await axios.post("http://127.0.0.1:8001/parser_1", {
await axios.post("https://allowlgroup.ru/api/8001/parser_1", {
time: time.value,
});

View File

@@ -0,0 +1,81 @@
<template>
<div
class="dark:bg-gray-800 mt-5 sm:m-5 bg-white p-4 hover:-translate-y-2 hover:shadow-2xl border-slate-100 rounded-xl transition"
>
<div class="flex flex-wrap items-center gap-2 w-full justify-between">
<!-- Поля ввода дат в одной строке -->
<div class="flex items-center gap-2 flex-grow max-w-120">
<span class="dark:text-neutral-300 whitespace-nowrap">с</span>
<DatePicker v-model="data_start" />
<span class="dark:text-neutral-300 whitespace-nowrap">по</span>
<DatePicker v-model="data_finish" />
</div>
<!-- Кнопка выгрузки -->
<button
class="dark:bg-orange-500 hover:dark:bg-orange-600 shadow text-white bg-sky-700 hover:bg-sky-900 rounded-xl px-2 min-h-11 cursor-pointer w-full sm:w-auto sm:min-w-40 flex-shrink-0"
@click="downloadAll"
>
Выгрузить
</button>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import axios from "axios";
import DatePicker from "./DatePicker.vue";
// Переменные для выгрузки с датами по умолчанию
const data_start = ref(getYesterdayDate());
const data_finish = ref(getTodayDate());
// Функция для получения вчерашней даты в формате YYYY-MM-DD
function getYesterdayDate() {
const date = new Date();
date.setDate(date.getDate() - 1);
return date.toISOString().split("T")[0];
}
// Функция для получения сегодняшней даты в формате YYYY-MM-DD
function getTodayDate() {
return new Date().toISOString().split("T")[0];
}
// Выгрузка
const downloadAll = async () => {
try {
const response = await axios.post(
// "http://127.0.0.1:8001/download_all",
"https://allowlgroup.ru/api/8001/download_all",
{
data_start: data_start.value,
data_finish: data_finish.value,
},
{
responseType: "blob",
},
);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute(
"download",
`documents_${data_start.value}_${data_finish.value}.zip`,
);
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error("Ошибка при выгрузке:", error);
}
};
</script>

View File

@@ -1,34 +0,0 @@
<template>
<div class="w-full sm:w-4/5 dark:text-neutral-300">
<div class="bg-white flex justify-between p-5 dark:bg-gray-800 ">
<h1>Результат:</h1>
<Time />
</div>
<div class="p-4">
<Stat
v-for="item in items"
:translation_text="item.translation_text"
:original_text="item.original_text"
:url="item.url"
:title="item.title"
:article_date="item.article_date"
:short_text="item.short_text"
:category="item.category"
:parsed_at="item.parsed_at"
:status = "item.status"
:viewed = "item.viewed"
:other = "item.other"
/>
</div>
</div>
</template>
<script setup>
import Stat from "./Stat.vue";
import Time from "./Time.vue";
defineProps({
items: Array,
});
</script>