добавление выгрузки
This commit is contained in:
15
src/App.vue
15
src/App.vue
@@ -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>
|
||||
|
||||
62
src/components/DatePicker.vue
Normal file
62
src/components/DatePicker.vue
Normal 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>
|
||||
@@ -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) {
|
||||
@@ -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>
|
||||
@@ -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],
|
||||
@@ -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
|
||||
<td>
|
||||
<button
|
||||
class="text-red-500 p-1 rounded-full hover:text-red-700 cursor-pointer"
|
||||
@click="delete_row(task.id)"
|
||||
>
|
||||
<button class="text-red-500 p-1 rounded-full hover:text-red-700 cursor-pointer" @click="delete_row(task.id)">x</button>
|
||||
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,
|
||||
});
|
||||
|
||||
81
src/components/Setings_downloads.vue
Normal file
81
src/components/Setings_downloads.vue
Normal 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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user