Initial commit
This commit is contained in:
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Cypress
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Vitest
|
||||
__screenshots__/
|
||||
|
||||
test-results/
|
||||
playwright-report/
|
||||
6
.vscode/extensions.json
vendored
Normal file
6
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"ms-playwright.playwright"
|
||||
]
|
||||
}
|
||||
57
README.md
Normal file
57
README.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# test
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Recommended Browser Setup
|
||||
|
||||
- Chromium-based browsers (Chrome, Edge, Brave, etc.):
|
||||
- [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)
|
||||
- [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters)
|
||||
- Firefox:
|
||||
- [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/)
|
||||
- [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/)
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Run End-to-End Tests with [Playwright](https://playwright.dev)
|
||||
|
||||
```sh
|
||||
# Install browsers for the first run
|
||||
npx playwright install
|
||||
|
||||
# When testing on CI, must build the project first
|
||||
npm run build
|
||||
|
||||
# Runs the end-to-end tests
|
||||
npm run test:e2e
|
||||
# Runs the tests only on Chromium
|
||||
npm run test:e2e -- --project=chromium
|
||||
# Runs the tests of a specific file
|
||||
npm run test:e2e -- tests/example.spec.ts
|
||||
# Runs the tests in debug mode
|
||||
npm run test:e2e -- --debug
|
||||
```
|
||||
8
e2e/vue.spec.js
Normal file
8
e2e/vue.spec.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
// See here how to get started:
|
||||
// https://playwright.dev/docs/intro
|
||||
test('visits the app root url', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page.locator('h1')).toHaveText('You did it!');
|
||||
})
|
||||
13
index.html
Normal file
13
index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/src/assets/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
8
jsconfig.json
Normal file
8
jsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
0
package-lock.json
generated
Normal file
0
package-lock.json
generated
Normal file
27
package.json
Normal file
27
package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "test",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.13.2",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"vue": "^3.5.25"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.57.0",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"vite": "^7.3.0",
|
||||
"vite-plugin-vue-devtools": "^8.0.5"
|
||||
}
|
||||
}
|
||||
110
playwright.config.js
Normal file
110
playwright.config.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import process from 'node:process'
|
||||
import { defineConfig, devices } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: './e2e',
|
||||
/* Maximum time one test can run for. */
|
||||
timeout: 30 * 1000,
|
||||
expect: {
|
||||
/**
|
||||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 5000,
|
||||
},
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||
actionTimeout: 0,
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: process.env.CI ? 'http://localhost:4173' : 'http://localhost:5173',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
|
||||
/* Only on CI systems run the tests headless */
|
||||
headless: !!process.env.CI,
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'firefox',
|
||||
use: {
|
||||
...devices['Desktop Firefox'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'webkit',
|
||||
use: {
|
||||
...devices['Desktop Safari'],
|
||||
},
|
||||
},
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: {
|
||||
// ...devices['Pixel 5'],
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: {
|
||||
// ...devices['iPhone 12'],
|
||||
// },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: {
|
||||
// channel: 'msedge',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: {
|
||||
// channel: 'chrome',
|
||||
// },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||
// outputDir: 'test-results/',
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: {
|
||||
/**
|
||||
* Use the dev server by default for faster feedback loop.
|
||||
* Use the preview server on CI for more realistic testing.
|
||||
* Playwright will re-use the local server if there is already a dev-server running.
|
||||
*/
|
||||
command: process.env.CI ? 'npm run preview' : 'npm run dev',
|
||||
port: process.env.CI ? 4173 : 5173,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
})
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
59
src/App.vue
Normal file
59
src/App.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<script setup>
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import My_naw from "./components/My_naw.vue";
|
||||
import Section from "./components/Section.vue";
|
||||
import Setings from "./components/Setings.vue";
|
||||
import Authe from "./components/Authe.vue";
|
||||
import axios from "axios";
|
||||
// Состояния страницы и данные для входа
|
||||
const currentPage = ref("admin-panel");
|
||||
const items = ref([]);
|
||||
|
||||
// Универсальная функция для получения данных
|
||||
const fetchData = async (url, targetRef) => {
|
||||
try {
|
||||
const { data } = await axios.get(url)
|
||||
targetRef.value = data
|
||||
} catch (err) {
|
||||
console.error(`Ошибка при получении данных с ${url}:`, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Обертки для вызова
|
||||
const loadItems = () => fetchData("http://45.129.78.228:8002/records_all", items) //http://127.0.0.1:8002/records_all http://45.129.78.228:8002/records_all
|
||||
|
||||
onMounted(() => {
|
||||
loadItems()
|
||||
})
|
||||
|
||||
watch(items, loadItems)
|
||||
|
||||
function handleUpdate(newValue) {
|
||||
currentPage.value = newValue; // изменение значения в родителе
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<Authe :currentPage="currentPage" @update="handleUpdate"/>
|
||||
<div v-if="currentPage === 'rezylt'">
|
||||
<div class="sm:flex">
|
||||
|
||||
<My_naw :currentPage="currentPage" @update="handleUpdate"/>
|
||||
<Section :items="items"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currentPage === 'setings'">
|
||||
<div class="sm:flex">
|
||||
<My_naw :currentPage="currentPage" @update="handleUpdate"/>
|
||||
<Setings/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Добавьте стили по необходимости */
|
||||
</style>
|
||||
BIN
src/assets/href.webp
Normal file
BIN
src/assets/href.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
1
src/assets/logo.svg
Normal file
1
src/assets/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
16
src/assets/main.css
Normal file
16
src/assets/main.css
Normal file
@@ -0,0 +1,16 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
/* Для темной темы */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: #111;
|
||||
/* другие стили для темной темы */
|
||||
}
|
||||
}
|
||||
|
||||
/* Для светлой темы (по умолчанию или явно) */
|
||||
@media (prefers-color-scheme: light) {
|
||||
body {
|
||||
background: #ccc; /* или любой другой цвет */
|
||||
}
|
||||
}
|
||||
92
src/components/Authe.vue
Normal file
92
src/components/Authe.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
import axios from "axios";
|
||||
|
||||
// Получение props
|
||||
const props = defineProps({
|
||||
currentPage: String,
|
||||
});
|
||||
|
||||
// Объявление события для обновления страницы
|
||||
const emit = defineEmits(["update"]);
|
||||
|
||||
const login = ref("");
|
||||
const password = ref("");
|
||||
const authError = ref(false);
|
||||
|
||||
// Функция авторизации
|
||||
const auth_my = async () => {
|
||||
try {
|
||||
const response = await axios.post("http://45.129.78.228:8004/login", {
|
||||
username: login.value,
|
||||
password: password.value,
|
||||
});
|
||||
if (response.data.message === "Login successful") {
|
||||
authError.value = false;
|
||||
emit("update", "rezylt");
|
||||
} else {
|
||||
authError.value = true;
|
||||
}
|
||||
} catch (err) {
|
||||
authError.value = true;
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
// Обработка глобального нажатия Enter
|
||||
const handleKeyDown = (event) => {
|
||||
if (event.key === "Enter") {
|
||||
auth_my();
|
||||
}
|
||||
};
|
||||
|
||||
// Добавляем глобальный слушатель при монтировании
|
||||
onMounted(() => {
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
});
|
||||
|
||||
// Удаляем при размонтировании
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
});
|
||||
|
||||
// Переход на стартовую страницу (сброс данных)
|
||||
function showStartPage() {
|
||||
emit("update", "admin-panel");
|
||||
login.value = "";
|
||||
password.value = "";
|
||||
authError.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="props.currentPage === 'admin-panel'"
|
||||
class="p-4 bg-white rounded shadow max-w-md mx-auto mt-10 dark:bg-gray-800 dark:text-neutral-300"
|
||||
>
|
||||
<h2>Вход для администратора</h2>
|
||||
<div class="mb-2">
|
||||
<label>Логин:</label>
|
||||
<input v-model="login" type="text" class="border p-1 w-full" />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label>Пароль:</label>
|
||||
<input v-model="password" type="password" class="border p-1 w-full" />
|
||||
</div>
|
||||
<button
|
||||
@click="auth_my"
|
||||
class="mr-2 bg-teal-600 hover:bg-teal-800 text-white px-4 py-2 rounded cursor-pointer"
|
||||
>
|
||||
Войти
|
||||
</button>
|
||||
<button
|
||||
@click="showStartPage"
|
||||
class="dark:bg-gray-600 dark:hover:bg-gray-700 bg-gray-500 hover:bg-gray-600 px-4 py-2 rounded cursor-pointer"
|
||||
>
|
||||
Отмена
|
||||
</button>
|
||||
<div v-if="authError" class="mt-2 text-red-600">
|
||||
Неверный логин или пароль
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
48
src/components/Mob_naw.vue
Normal file
48
src/components/Mob_naw.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<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>
|
||||
53
src/components/My_naw.vue
Normal file
53
src/components/My_naw.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class=" sticky top-0 w-full sm:w-1/5 sm:h-screen bg-white z-10 pt-5 md:p-5 dark:bg-gray-800">
|
||||
<!-- Фиксированный левый блок -->
|
||||
<div v-if="currentPage === 'rezylt'" class="flex sm:flex-col">
|
||||
<button
|
||||
@click="ValueRezylt"
|
||||
class="bg-gray-600 w-full text-white min-w-30 px-4 py-2 rounded cursor-pointer"
|
||||
>
|
||||
Результат
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="ValueSeting"
|
||||
class="bg-gray-300 sm:mt-3 w-full min-w-30 hover:text-white hover:bg-gray-600 px-4 py-2 rounded cursor-pointer"
|
||||
>
|
||||
Настройка
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="currentPage === 'setings'" class="flex sm:flex-col">
|
||||
<button
|
||||
@click="ValueRezylt"
|
||||
class="bg-gray-300 w-full min-w-30 hover:text-white hover:bg-gray-600 px-4 py-2 rounded cursor-pointer"
|
||||
>
|
||||
Результат
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="ValueSeting"
|
||||
class="bg-gray-600 sm:mt-3 w-full text-white min-w-30 px-4 py-2 rounded cursor-pointer"
|
||||
>
|
||||
Настройка
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
currentPage: String,
|
||||
});
|
||||
|
||||
// Макросы Vue 3.3+ не требуют явного импорта
|
||||
const emit = defineEmits(["update"]);
|
||||
|
||||
function ValueSeting() {
|
||||
emit("update", "setings");
|
||||
}
|
||||
|
||||
function ValueRezylt() {
|
||||
emit("update", "rezylt");
|
||||
}
|
||||
</script>
|
||||
35
src/components/Section.vue
Normal file
35
src/components/Section.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<!-- AdminLogin.vue -->
|
||||
<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>
|
||||
240
src/components/Setings.vue
Normal file
240
src/components/Setings.vue
Normal file
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div class="w-full sm:w-4/5 dark:text-neutral-300">
|
||||
<!-- Ввод времени -->
|
||||
<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"
|
||||
/>
|
||||
<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"
|
||||
>
|
||||
Парсить 1 источник
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="dark:bg-orange-500 hover:dark:bg-orange-600 sm:ml-2 shadow mt-1 sm:mt-0 text-white w-full sm:max-w-50 bg-sky-700 hover:bg-sky-900 rounded-xl px-2 min-h-11 cursor-pointer"
|
||||
@click="start_parser_2"
|
||||
>
|
||||
Парсить 2 источник
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Список источников и промт -->
|
||||
<div
|
||||
class="dark:bg-gray-800 sm:m-5 bg-white p-4 hover:-translate-y-2 hover:shadow-2xl border-slate-100 rounded-xl transition"
|
||||
>
|
||||
<div
|
||||
class="flex-colum sm:flex items-center mb-4 border-slate-900 justify-between"
|
||||
>
|
||||
<div class="w-full sm:max-w-60 flex">
|
||||
<button
|
||||
v-for="(source, index) in sources"
|
||||
:key="source.name"
|
||||
@click="selectSource(index)"
|
||||
class="dark:bg-neutral-500 hover:dark:bg-neutral-600 p-2 mr-2 rounded-xl bg-sky-700 hover:bg-sky-900 w-1/2 text-white cursor-pointer"
|
||||
>
|
||||
{{ source.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Кнопка сохранения источников -->
|
||||
<button
|
||||
class="shadow mt-1 sm:mt-0 text-white bg-green-500 hover:bg-green-600 rounded-xl w-full sm:max-w-60 px-2 min-h-11 cursor-pointer"
|
||||
@click="saveSources"
|
||||
>
|
||||
Сохранить изменения
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Редактируемый промт -->
|
||||
<div class=" ">
|
||||
<textarea
|
||||
v-model="promt"
|
||||
class="w-full min-h-100 rounded-xl p-2 border-2 border-neutral-300"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Список логов -->
|
||||
<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"
|
||||
>
|
||||
<!-- Кнопка для переключения -->
|
||||
<button
|
||||
@click="toggleLogs"
|
||||
class="dark:bg-orange-500 hover:dark:bg-orange-600 shadow text-white bg-sky-700 w-full hover:bg-sky-900 rounded-xl px-2 min-h-11 cursor-pointer"
|
||||
>
|
||||
{{ showLogs ? "Скрыть логи" : "Показать логи" }}
|
||||
</button>
|
||||
|
||||
<!-- Блок логов, показывается или скрывается -->
|
||||
<div
|
||||
class="border-1 border-neutral-300 p-2 mt-2 rounded-xl"
|
||||
v-if="showLogs"
|
||||
>
|
||||
<div v-for="(log, index) in logs" :key="index">
|
||||
{{ log }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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"
|
||||
>
|
||||
<!-- Кнопка для переключения -->
|
||||
<button
|
||||
@click="toggletask"
|
||||
class="dark:bg-orange-500 hover:dark:bg-orange-600 shadow text-white bg-sky-700 w-full hover:bg-sky-900 rounded-xl px-2 min-h-11 cursor-pointer"
|
||||
>
|
||||
{{ showtask ? "Скрыть задачи" : "Показать задачи" }}
|
||||
</button>
|
||||
<div
|
||||
class="border-1 border-neutral-300 p-2 mt-2 rounded-xl overflow-x-auto"
|
||||
v-if="showtask"
|
||||
>
|
||||
<table class="w-full min-w-max">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-2 py-2 text-left">Status</th>
|
||||
<th class="px-2 py-2 text-left">Source URL</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="task in tasks" :key="task.id">
|
||||
<td
|
||||
class="px-2 py-2 whitespace-nowrap overflow-hidden overflow-ellipsis max-w-full"
|
||||
>
|
||||
{{ task.status }}
|
||||
</td>
|
||||
<td
|
||||
class="px-2 py-2 whitespace-nowrap overflow-hidden overflow-ellipsis max-w-full"
|
||||
>
|
||||
{{ task.source_url }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import axios from "axios";
|
||||
|
||||
const sources = ref([
|
||||
{ name: "source1", url: "eee", prompt: "" },
|
||||
{ name: "source2", url: "https://example2.com/api", prompt: "" },
|
||||
]);
|
||||
|
||||
const currentSource = ref(sources.value[0]);
|
||||
const time = ref("");
|
||||
const promt = ref("Начальный текст");
|
||||
const logs = ref([]);
|
||||
const tasks = ref([]);
|
||||
const showLogs = ref(false); // скрыто по умолчанию
|
||||
const showtask = ref(false); // скрыто по умолчанию
|
||||
|
||||
// функция для переключения видимости логов
|
||||
function toggleLogs() {
|
||||
showLogs.value = !showLogs.value;
|
||||
if (showLogs.value) {
|
||||
load_log();
|
||||
}
|
||||
}
|
||||
|
||||
const load_log = async () => {
|
||||
try {
|
||||
const response = await axios.get("http://45.129.78.228:8001/logs");
|
||||
logs.value = response.data.logs;
|
||||
} catch (error) {
|
||||
console.error("Ошибка при загрузке настроек:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// функция для переключения видимости задач
|
||||
function toggletask() {
|
||||
showtask.value = !showtask.value;
|
||||
if (showtask.value) {
|
||||
loadTasks();
|
||||
}
|
||||
}
|
||||
|
||||
const loadTasks = async () => {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
"http://45.129.78.228:8001/get_tasks_offset",
|
||||
);
|
||||
// Предположим, что ответ уже массив задач
|
||||
tasks.value = response.data; // или response.data если это массив
|
||||
} catch (error) {
|
||||
console.error("Ошибка при загрузке задач:", error);
|
||||
}
|
||||
};
|
||||
// Загрузка настроек при монтировании
|
||||
onMounted(() => {
|
||||
loadSettings();
|
||||
// load_log();
|
||||
});
|
||||
|
||||
// watch(logs, load_log);
|
||||
|
||||
// Получение настроек с сервера
|
||||
const loadSettings = async () => {
|
||||
try {
|
||||
const response = await axios.get("http://45.129.78.228:8001/settings");
|
||||
sources.value = response.data.sources;
|
||||
currentSource.value = sources.value[0];
|
||||
promt.value = currentSource.value.prompt;
|
||||
} catch (error) {
|
||||
console.error("Ошибка при загрузке настроек:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Выбор источника
|
||||
const selectSource = (index) => {
|
||||
currentSource.value = sources.value[index];
|
||||
promt.value = currentSource.value.prompt;
|
||||
};
|
||||
|
||||
// Сохранение источников
|
||||
const saveSources = async () => {
|
||||
try {
|
||||
await axios.post("http://45.129.78.228:8001/settings", {
|
||||
name: currentSource.value.name,
|
||||
url: "",
|
||||
prompt: promt.value,
|
||||
});
|
||||
// Здесь можно отправить на сервер, если нужно
|
||||
loadSettings();
|
||||
} catch (error) {
|
||||
console.error("Ошибка при сохранении источников:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Запуск парсинга 1
|
||||
const start_parser_1 = async () => {
|
||||
try {
|
||||
await axios.post("http://45.129.78.228:8001/parser_1", {
|
||||
time: time.value,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Ошибка запроса parser_1:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Запуск парсинга 2
|
||||
const start_parser_2 = async () => {
|
||||
try {
|
||||
await axios.post("http://45.129.78.228:8001/parser_2");
|
||||
} catch (error) {
|
||||
console.error("Ошибка запроса parser_2:", error);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
106
src/components/Stat.vue
Normal file
106
src/components/Stat.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from "vue";
|
||||
import axios from "axios";
|
||||
|
||||
const props = defineProps({
|
||||
url: String,
|
||||
parsed_at: String,
|
||||
title: String,
|
||||
category: String,
|
||||
short_text: String,
|
||||
article_date: String,
|
||||
original_text: String,
|
||||
translation_text: String,
|
||||
other: String,
|
||||
viewed: Boolean,
|
||||
status: Boolean,
|
||||
});
|
||||
|
||||
//Код для раскрытия/скрытия карточки
|
||||
const isOpen = ref(false);
|
||||
const cardRef = ref(null);
|
||||
|
||||
function toggle() {
|
||||
isOpen.value = true; //!isOpen.value;
|
||||
}
|
||||
|
||||
function handleClickOutside(event) {
|
||||
if (cardRef.value && !cardRef.value.contains(event.target)) {
|
||||
isOpen.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const viewed_b = async () => {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
"http://45.129.78.228:8002/update_viewed_status",
|
||||
null,
|
||||
{
|
||||
params: {
|
||||
url: props.url,
|
||||
viewed: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener("click", handleClickOutside);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener("click", handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 hover:-translate-y-2 hover:shadow-2xl rounded-xl transition pb-1 mb-3"
|
||||
:class="{ 'opacity-50': props.viewed }"
|
||||
><!--:class="{ 'opacity-50': props.viewed }" обработчик прочитанности-->
|
||||
<div ref="cardRef" class="cursor-pointer" @click="toggle">
|
||||
<div class="flex justify-between p-3">
|
||||
<p>{{ article_date }}</p>
|
||||
<a
|
||||
:href="url"
|
||||
class="max-w-100 text-blue-600 dark:text-gray-400 hover:text-blue-800 underline flex"
|
||||
style="overflow: hidden; white-space: nowrap; text-overflow: ellipsis"
|
||||
>
|
||||
<img
|
||||
src="/src/assets/href.webp"
|
||||
class="w-5 h-6 mr-2"
|
||||
/>
|
||||
{{ url }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="m-3 dark:bg-gray-900 shadow rounded-xl p-3">
|
||||
<p>{{ title }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Вариант с Tailwind: показывать/скрывать по состоянию -->
|
||||
<div v-show="isOpen" class="transition-all">
|
||||
<div class="m-3 dark:bg-gray-900 shadow rounded-xl p-3">
|
||||
<p>{{ short_text }}</p>
|
||||
</div>
|
||||
<div class="m-3 dark:bg-gray-900 shadow rounded-xl p-3">
|
||||
<p>{{ translation_text }}</p>
|
||||
</div>
|
||||
<div class="m-3 dark:bg-gray-900 shadow rounded-xl p-3">
|
||||
<p>{{ category }}</p>
|
||||
</div>
|
||||
<div class="m-3 dark:bg-gray-900 shadow rounded-xl p-3">
|
||||
<p>{{ original_text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="ml-4 shadow mb-5 bg-lime-500 dark:bg-orange-500 hover:dark:bg-orange-600 hover:dark:text-neutral-50 hover:bg-lime-300 hover:text-stone-900 rounded-xl w-40 h-10 text-white cursor-pointer"
|
||||
@click="viewed_b"
|
||||
>
|
||||
Прочитано
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
45
src/components/Time.vue
Normal file
45
src/components/Time.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>Дата и время в Пекине: <br>{{ currentDateTime }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currentDateTime: '', // строка с текущими датой и временем
|
||||
timer: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateTime() {
|
||||
const options = {
|
||||
timeZone: 'Asia/Shanghai',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
};
|
||||
const formatter = new Intl.DateTimeFormat('zh-CN', options);
|
||||
const parts = formatter.formatToParts(new Date());
|
||||
|
||||
const dateParts = {};
|
||||
parts.forEach(({ type, value }) => {
|
||||
dateParts[type] = value;
|
||||
});
|
||||
|
||||
this.currentDateTime = `${dateParts.year}-${dateParts.month}-${dateParts.day} ${dateParts.hour}:${dateParts.minute}:${dateParts.second}`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.updateTime();
|
||||
this.timer = setInterval(this.updateTime, 1000);
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
6
src/main.js
Normal file
6
src/main.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import './assets/main.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
15
vite.config.js
Normal file
15
vite.config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import vueDevTools from "vite-plugin-vue-devtools";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue(), vueDevTools(), tailwindcss()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user