mirror of
https://github.com/hpware/news-analyze.git
synced 2025-06-23 15:51:01 +08:00
feat: update README and various components for improved formatting and error handling; enhance login functionality with password encryption
This commit is contained in:
parent
ee13960f0b
commit
fd2ba525f9
@ -22,6 +22,7 @@ App Design: [Freeform](https://www.icloud.com/freeform/026AxB798cViZ9jJ2DkNsXUCQ
|
|||||||
你會看到許多觀點,但不知道這些新聞為什麼會寫比較偏見的文章。
|
你會看到許多觀點,但不知道這些新聞為什麼會寫比較偏見的文章。
|
||||||
|
|
||||||
## Inspired by
|
## Inspired by
|
||||||
|
|
||||||
- puter.com
|
- puter.com
|
||||||
- Perplexity
|
- Perplexity
|
||||||
- Ground.news
|
- Ground.news
|
||||||
@ -86,4 +87,5 @@ App Design: [Freeform](https://www.icloud.com/freeform/026AxB798cViZ9jJ2DkNsXUCQ
|
|||||||
7. Open `http://localhost:3000` in your browser.
|
7. Open `http://localhost:3000` in your browser.
|
||||||
|
|
||||||
### For scaping
|
### For scaping
|
||||||
|
|
||||||
First, Run `ps1 clone-env.ps1` or `bash clone-env.sh` to clone the `.env` file to the `scraping` folder, then cd into the `scraping` folder. Run `python main.py` to start scraping in Google News.
|
First, Run `ps1 clone-env.ps1` or `bash clone-env.sh` to clone the `.env` file to the `scraping` folder, then cd into the `scraping` folder. Run `python main.py` to start scraping in Google News.
|
@ -1,53 +1,53 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title: string
|
title: string;
|
||||||
initialX?: number
|
initialX?: number;
|
||||||
initialY?: number
|
initialY?: number;
|
||||||
width?: string
|
width?: string;
|
||||||
height?: string
|
height?: string;
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(["close"]);
|
||||||
|
|
||||||
const isDragging = ref(false)
|
const isDragging = ref(false);
|
||||||
const position = ref({
|
const position = ref({
|
||||||
x: props.initialX || Math.floor(window.innerWidth / 2) - Math.random() * 200,
|
x: props.initialX || Math.floor(window.innerWidth / 2) - Math.random() * 200,
|
||||||
y: props.initialY || Math.floor(window.innerHeight / 2) - Math.random() * 10
|
y: props.initialY || Math.floor(window.innerHeight / 2) - Math.random() * 10,
|
||||||
})
|
});
|
||||||
const offset = ref({ x: 0, y: 0 })
|
const offset = ref({ x: 0, y: 0 });
|
||||||
|
|
||||||
const startDrag = (e: MouseEvent) => {
|
const startDrag = (e: MouseEvent) => {
|
||||||
isDragging.value = true
|
isDragging.value = true;
|
||||||
offset.value = {
|
offset.value = {
|
||||||
x: e.clientX - position.value.x,
|
x: e.clientX - position.value.x,
|
||||||
y: e.clientY - position.value.y
|
y: e.clientY - position.value.y,
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
const doDrag = (e: MouseEvent) => {
|
const doDrag = (e: MouseEvent) => {
|
||||||
if (isDragging.value) {
|
if (isDragging.value) {
|
||||||
position.value = {
|
position.value = {
|
||||||
x: e.clientX - offset.value.x,
|
x: e.clientX - offset.value.x,
|
||||||
y: e.clientY - offset.value.y
|
y: e.clientY - offset.value.y,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const stopDrag = () => {
|
const stopDrag = () => {
|
||||||
isDragging.value = false
|
isDragging.value = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('mousemove', doDrag)
|
document.addEventListener("mousemove", doDrag);
|
||||||
document.addEventListener('mouseup', stopDrag)
|
document.addEventListener("mouseup", stopDrag);
|
||||||
})
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
document.removeEventListener('mousemove', doDrag)
|
document.removeEventListener("mousemove", doDrag);
|
||||||
document.removeEventListener('mouseup', stopDrag)
|
document.removeEventListener("mouseup", stopDrag);
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -56,7 +56,7 @@ onUnmounted(() => {
|
|||||||
left: `${position.x}px`,
|
left: `${position.x}px`,
|
||||||
top: `${position.y}px`,
|
top: `${position.y}px`,
|
||||||
width: props.width || '400px',
|
width: props.width || '400px',
|
||||||
height: props.height || '300px'
|
height: props.height || '300px',
|
||||||
}"
|
}"
|
||||||
class="fixed bg-white dark:bg-gray-800 rounded-md shadow-lg overflow-hidden flex flex-col"
|
class="fixed bg-white dark:bg-gray-800 rounded-md shadow-lg overflow-hidden flex flex-col"
|
||||||
>
|
>
|
||||||
|
@ -1,14 +1,50 @@
|
|||||||
|
<script setup lamng="ts">
|
||||||
|
const userAccount = ref("");
|
||||||
|
const userPassword = ref("");
|
||||||
|
const crypto = Crypto;
|
||||||
|
const submitUserPassword = async () => {
|
||||||
|
// Encrypt password during transit
|
||||||
|
const sha512Passwordify = crypto.createHash("sha512");
|
||||||
|
sha512Passwordify.update(userPassword.value);
|
||||||
|
const sha512Password = sha512Passwordify.digest("hex");
|
||||||
|
|
||||||
|
// Log user & password
|
||||||
|
console.log(userAccount.value);
|
||||||
|
console.log(sha512Password);
|
||||||
|
|
||||||
|
// Send data.
|
||||||
|
const sendData = fetch("/api/user/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
username: userAccount.value,
|
||||||
|
password: sha512Password,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center justify-center h-full">
|
<div class="flex flex-col items-center justify-center h-full">
|
||||||
<form class="flex flex-col items-center justify-center h-full">
|
<div
|
||||||
|
class="flex flex-col items-center justify-center h-full"
|
||||||
|
>
|
||||||
<div class="text-xl mb-4 text-bold">Login / Register</div>
|
<div class="text-xl mb-4 text-bold">Login / Register</div>
|
||||||
|
|
||||||
<input type="text" placeholder="Username" class="mb-2 p-2 border rounded" />
|
<input
|
||||||
<input type="password" placeholder="Password" class="p-2 border rounded mb-2" />
|
type="text"
|
||||||
<button class="bg-black text-white p-2 rounded transition duration-200">
|
placeholder="Username"
|
||||||
|
class="mb-2 p-2 border rounded"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
class="p-2 border rounded mb-2"
|
||||||
|
/>
|
||||||
|
<button class="bg-black text-white p-2 rounded transition duration-200" @click="submitUserPassword">
|
||||||
Log In
|
Log In
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
@ -1,4 +1,4 @@
|
|||||||
version: '3.8'
|
version: "3.8"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
newsanalyze-service:
|
newsanalyze-service:
|
||||||
|
13
error.vue
13
error.vue
@ -4,9 +4,16 @@ const { t } = useI18n();
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<NuxtLayout>
|
<NuxtLayout>
|
||||||
<div class="flex flex-col justify-center align-center text-center absolute inset-0 h-screen">
|
<div
|
||||||
<span class="text-7xl m-4 m-1 mb-0 text-center align-center justify-center">{{ error.statusCode }}</span>
|
class="flex flex-col justify-center align-center text-center absolute inset-0 h-screen"
|
||||||
<span class="text-2xl text-center align-center justify-center">{{ error.statusMessage }}</span>
|
>
|
||||||
|
<span
|
||||||
|
class="text-7xl m-4 m-1 mb-0 text-center align-center justify-center"
|
||||||
|
>{{ error.statusCode }}</span
|
||||||
|
>
|
||||||
|
<span class="text-2xl text-center align-center justify-center">{{
|
||||||
|
error.statusMessage
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-screen"></div>
|
<div class="h-screen"></div>
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// No layout
|
// No layout
|
||||||
|
|
||||||
@ -28,7 +27,12 @@ import ProgressComponent from "~/components/ui/progress/Progress.vue";
|
|||||||
import HoverCardComponent from "~/components/ui/hover-card/HoverCard.vue";
|
import HoverCardComponent from "~/components/ui/hover-card/HoverCard.vue";
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import { ComputerDesktopIcon, UserIcon, LanguageIcon, ChevronRightIcon } from "@heroicons/vue/24/outline";
|
import {
|
||||||
|
ComputerDesktopIcon,
|
||||||
|
UserIcon,
|
||||||
|
LanguageIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
} from "@heroicons/vue/24/outline";
|
||||||
|
|
||||||
// i18n
|
// i18n
|
||||||
const { t, locale, locales } = useI18n();
|
const { t, locale, locales } = useI18n();
|
||||||
@ -80,28 +84,28 @@ const openWindow = (windowName?: string) => {
|
|||||||
}
|
}
|
||||||
console.log(windowName);
|
console.log(windowName);
|
||||||
menuOpen.value = false;
|
menuOpen.value = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
const unMinWindow = (windowName?: string) => {
|
const unMinWindow = (windowName?: string) => {
|
||||||
console.log(windowName);
|
console.log(windowName);
|
||||||
}
|
};
|
||||||
|
|
||||||
// menus
|
// menus
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ name: "Hot News", windowName: "hotnews"} ,
|
{ name: "Hot News", windowName: "hotnews" },
|
||||||
{ name: "News", windowName: "news"},
|
{ name: "News", windowName: "news" },
|
||||||
{ name: "Sources", windowName: "sources"},
|
{ name: "Sources", windowName: "sources" },
|
||||||
{ name: 'About This Website', windowName: "about"},
|
{ name: "About This Website", windowName: "about" },
|
||||||
{ name: 'Settings', windowName: "settings"},
|
{ name: "Settings", windowName: "settings" },
|
||||||
{ name: 'Leave', windowName: "leave"},
|
{ name: "Leave", windowName: "leave" },
|
||||||
]
|
];
|
||||||
const toggleMenu = () => {
|
const toggleMenu = () => {
|
||||||
menuOpen.value = !menuOpen.value
|
menuOpen.value = !menuOpen.value;
|
||||||
}
|
};
|
||||||
// Lang Menu
|
// Lang Menu
|
||||||
const toggleLangMenu = () => {
|
const toggleLangMenu = () => {
|
||||||
langMenuOpen.value = !langMenuOpen.value
|
langMenuOpen.value = !langMenuOpen.value;
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
@ -109,23 +113,38 @@ const toggleLangMenu = () => {
|
|||||||
>
|
>
|
||||||
<!--Menu container-->
|
<!--Menu container-->
|
||||||
<div class="flex flex-row g-2 text-gray-400 text-white z-9999">
|
<div class="flex flex-row g-2 text-gray-400 text-white z-9999">
|
||||||
<button @click="toggleMenu" class="w-8 h-8 text-white hover:text-blue-500 transition-all duration-100 flex flex-row">
|
<button
|
||||||
<ComputerDesktopIcon/>
|
@click="toggleMenu"
|
||||||
|
class="w-8 h-8 text-white hover:text-blue-500 transition-all duration-100 flex flex-row"
|
||||||
|
>
|
||||||
|
<ComputerDesktopIcon />
|
||||||
</button>
|
</button>
|
||||||
<span class="ml-1 mr-2 text-[20px]">|</span>
|
<span class="ml-1 mr-2 text-[20px]">|</span>
|
||||||
<!--navbar icons for min and max application window-->
|
<!--navbar icons for min and max application window-->
|
||||||
<button class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100">
|
<button
|
||||||
</button>
|
class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100"
|
||||||
<div v-for="item in currentNavBar" :key="item.name" class="flex flex-row items-center gap-x-2 hover:bg-gray-100 transition-all duration-100 px-4 py-2 cursor-pointer">
|
></button>
|
||||||
<button @click="unMinWindow(item.windowAssociated)" class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100">
|
<div
|
||||||
|
v-for="item in currentNavBar"
|
||||||
|
:key="item.name"
|
||||||
|
class="flex flex-row items-center gap-x-2 hover:bg-gray-100 transition-all duration-100 px-4 py-2 cursor-pointer"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
@click="unMinWindow(item.windowAssociated)"
|
||||||
|
class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100"
|
||||||
|
>
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
<span v-if="item.flash" class="animate-ping absolute inline-flex h-3 w-3 rounded-full bg-red-400 opacity-75"></span>
|
<span
|
||||||
<span v-if="item.icon" :class="item.icon">
|
v-if="item.flash"
|
||||||
</span>
|
class="animate-ping absolute inline-flex h-3 w-3 rounded-full bg-red-400 opacity-75"
|
||||||
|
></span>
|
||||||
|
<span v-if="item.icon" :class="item.icon"> </span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center align-middle justify-center text-white">{{ currentDate }}</div>
|
<div class="text-center align-middle justify-center text-white">
|
||||||
|
{{ currentDate }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full h-[2.5em]"></div>
|
<div class="w-full h-[2.5em]"></div>
|
||||||
<!--Menu-->
|
<!--Menu-->
|
||||||
@ -133,9 +152,15 @@ const toggleLangMenu = () => {
|
|||||||
enter-active-class="animate__animated animate__fadeInDown animate_fast03"
|
enter-active-class="animate__animated animate__fadeInDown animate_fast03"
|
||||||
leave-active-class="animate__animated animate__fadeOutUp animate_fast03"
|
leave-active-class="animate__animated animate__fadeOutUp animate_fast03"
|
||||||
>
|
>
|
||||||
<div class="m-2 p-2 bg-gray-800 shadow-lg w-fit rounded-[10px] v-9998" v-if="menuOpen">
|
<div
|
||||||
|
class="m-2 p-2 bg-gray-800 shadow-lg w-fit rounded-[10px] v-9998"
|
||||||
|
v-if="menuOpen"
|
||||||
|
>
|
||||||
<div v-for="item in menuItems" :key="item.name" class="">
|
<div v-for="item in menuItems" :key="item.name" class="">
|
||||||
<button @click="openWindow(item.windowName)" class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100">
|
<button
|
||||||
|
@click="openWindow(item.windowName)"
|
||||||
|
class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100"
|
||||||
|
>
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
<ChevronRightIcon class="w-4 h-4 justify-center align-center" />
|
<ChevronRightIcon class="w-4 h-4 justify-center align-center" />
|
||||||
</button>
|
</button>
|
||||||
@ -146,18 +171,21 @@ const toggleLangMenu = () => {
|
|||||||
<div
|
<div
|
||||||
class="flex flex-col justify-center align-center text-center absolute w-full h-screen inset-x-0 inset-y-0 z-[-1]"
|
class="flex flex-col justify-center align-center text-center absolute w-full h-screen inset-x-0 inset-y-0 z-[-1]"
|
||||||
id="desktop"
|
id="desktop"
|
||||||
>
|
></div>
|
||||||
</div>
|
<slot />
|
||||||
<slot/>
|
<!--Footer-->
|
||||||
<!--Footer-->
|
|
||||||
<div
|
<div
|
||||||
class="absolute w-[calc(100% - 5px)] inset-x-0 bottom-0 mx-[1.5px] p-3 justify-between align-center flex flex-row"
|
class="absolute w-[calc(100% - 5px)] inset-x-0 bottom-0 mx-[1.5px] p-3 justify-between align-center flex flex-row"
|
||||||
>
|
>
|
||||||
<div class="">
|
<div class="">
|
||||||
<!--Lang-->
|
<!--Lang-->
|
||||||
<span>Lang: </span>
|
<span>Lang: </span>
|
||||||
<span class="text-lg">{{ t("localeflag") }}</span>
|
<span class="text-lg">{{ t("localeflag") }}</span
|
||||||
<button class="w-4 h-4 hover:text-blue-200 transition-all duration-100" @click="toggleLangMenu">
|
>
|
||||||
|
<button
|
||||||
|
class="w-4 h-4 hover:text-blue-200 transition-all duration-100"
|
||||||
|
@click="toggleLangMenu"
|
||||||
|
>
|
||||||
<LanguageIcon />
|
<LanguageIcon />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -170,8 +198,13 @@ const toggleLangMenu = () => {
|
|||||||
<span class="text-sm">{{ new Date().getFullYear() }} © yh</span>
|
<span class="text-sm">{{ new Date().getFullYear() }} © yh</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
<button @click="openWindow('login')" class="w-8 h-8 text-gray-400 flex flex-row">
|
<button
|
||||||
<UserIcon class="w-8 h-8 text-gray-400 hover:text-blue-500 transition-all duration-100" />
|
@click="openWindow('login')"
|
||||||
|
class="w-8 h-8 text-gray-400 flex flex-row"
|
||||||
|
>
|
||||||
|
<UserIcon
|
||||||
|
class="w-8 h-8 text-gray-400 hover:text-blue-500 transition-all duration-100"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,9 +9,15 @@ export default defineNuxtConfig({
|
|||||||
"/go/**": { ssr: true },
|
"/go/**": { ssr: true },
|
||||||
"/find/**": { ssr: true },
|
"/find/**": { ssr: true },
|
||||||
// Send ZIP bombs to troll bots
|
// Send ZIP bombs to troll bots
|
||||||
"/wp-admin/**": { redirect: "https://s3.yhw.tw/data/def-zip-bomb/wp-admin.php.zip" },
|
"/wp-admin/**": {
|
||||||
"/xmlrpc.php": { redirect: "https://s3.yhw.tw/data/def-zip-bomb/xmlrpc.php.zip" },
|
redirect: "https://s3.yhw.tw/data/def-zip-bomb/wp-admin.php.zip",
|
||||||
"/wp-login.php": { redirect: "https://s3.yhw.tw/data/def-zip-bomb/wp-login.php.zip" },
|
},
|
||||||
|
"/xmlrpc.php": {
|
||||||
|
redirect: "https://s3.yhw.tw/data/def-zip-bomb/xmlrpc.php.zip",
|
||||||
|
},
|
||||||
|
"/wp-login.php": {
|
||||||
|
redirect: "https://s3.yhw.tw/data/def-zip-bomb/wp-login.php.zip",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
css: ["~/styles/main.css"],
|
css: ["~/styles/main.css"],
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// No layout
|
// No layout
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
@ -38,9 +37,13 @@ import DialogComponent from "~/components/ui/dialog/Dialog.vue";
|
|||||||
import ProgressComponent from "~/components/ui/progress/Progress.vue";
|
import ProgressComponent from "~/components/ui/progress/Progress.vue";
|
||||||
import HoverCardComponent from "~/components/ui/hover-card/HoverCard.vue";
|
import HoverCardComponent from "~/components/ui/hover-card/HoverCard.vue";
|
||||||
|
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import { ComputerDesktopIcon, UserIcon, LanguageIcon, ChevronRightIcon } from "@heroicons/vue/24/outline";
|
import {
|
||||||
|
ComputerDesktopIcon,
|
||||||
|
UserIcon,
|
||||||
|
LanguageIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
} from "@heroicons/vue/24/outline";
|
||||||
|
|
||||||
// i18n
|
// i18n
|
||||||
const { t, locale, locales } = useI18n();
|
const { t, locale, locales } = useI18n();
|
||||||
@ -94,28 +97,28 @@ const openWindow = (windowName?: string) => {
|
|||||||
}
|
}
|
||||||
console.log(windowName);
|
console.log(windowName);
|
||||||
menuOpen.value = false;
|
menuOpen.value = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
const unMinWindow = (windowName?: string) => {
|
const unMinWindow = (windowName?: string) => {
|
||||||
console.log(windowName);
|
console.log(windowName);
|
||||||
}
|
};
|
||||||
|
|
||||||
// menus
|
// menus
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ name: "Hot News", windowName: "hotnews"} ,
|
{ name: "Hot News", windowName: "hotnews" },
|
||||||
{ name: "News", windowName: "news"},
|
{ name: "News", windowName: "news" },
|
||||||
{ name: "Sources", windowName: "sources"},
|
{ name: "Sources", windowName: "sources" },
|
||||||
{ name: 'About This Website', windowName: "about"},
|
{ name: "About This Website", windowName: "about" },
|
||||||
{ name: 'Settings', windowName: "settings"},
|
{ name: "Settings", windowName: "settings" },
|
||||||
{ name: 'Leave', windowName: "leave"},
|
{ name: "Leave", windowName: "leave" },
|
||||||
]
|
];
|
||||||
const toggleMenu = () => {
|
const toggleMenu = () => {
|
||||||
menuOpen.value = !menuOpen.value
|
menuOpen.value = !menuOpen.value;
|
||||||
}
|
};
|
||||||
// Lang Menu
|
// Lang Menu
|
||||||
const toggleLangMenu = () => {
|
const toggleLangMenu = () => {
|
||||||
langMenuOpen.value = !langMenuOpen.value
|
langMenuOpen.value = !langMenuOpen.value;
|
||||||
}
|
};
|
||||||
|
|
||||||
// values
|
// values
|
||||||
const activeWindows = ref<associAppWindowInterface>([]);
|
const activeWindows = ref<associAppWindowInterface>([]);
|
||||||
@ -123,7 +126,9 @@ const activeWindows = ref<associAppWindowInterface>([]);
|
|||||||
// ?opemapp= component
|
// ?opemapp= component
|
||||||
const openApp = ref(false);
|
const openApp = ref(false);
|
||||||
const openAppId = ref();
|
const openAppId = ref();
|
||||||
watch(() => route.query.openapp, (newVal) => {
|
watch(
|
||||||
|
() => route.query.openapp,
|
||||||
|
(newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
openApp.value = true;
|
openApp.value = true;
|
||||||
openAppId.value = newVal;
|
openAppId.value = newVal;
|
||||||
@ -133,17 +138,28 @@ watch(() => route.query.openapp, (newVal) => {
|
|||||||
query: {},
|
query: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const associAppWindow = [
|
const associAppWindow = [
|
||||||
{ name: "hotnews", id: "1", title: "Hot News", component: HotNewsWindow, width: "700px", height: "500px" },
|
{
|
||||||
|
name: "hotnews",
|
||||||
|
id: "1",
|
||||||
|
title: "Hot News",
|
||||||
|
component: HotNewsWindow,
|
||||||
|
width: "700px",
|
||||||
|
height: "500px",
|
||||||
|
},
|
||||||
{ name: "login", id: "2", title: "Login", component: LoginWindow },
|
{ name: "login", id: "2", title: "Login", component: LoginWindow },
|
||||||
]
|
];
|
||||||
|
|
||||||
const findAndOpenWindow = (windowName: string) => {
|
const findAndOpenWindow = (windowName: string) => {
|
||||||
const app = associAppWindow.find((app) => app.name === windowName);
|
const app = associAppWindow.find((app) => app.name === windowName);
|
||||||
// Prevent dual logins
|
// Prevent dual logins
|
||||||
if (windowName === "login" && activeWindows.value.some((window) => window.name === "login")) {
|
if (
|
||||||
|
windowName === "login" &&
|
||||||
|
activeWindows.value.some((window) => window.name === "login")
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (app) {
|
if (app) {
|
||||||
@ -161,7 +177,9 @@ const findAndOpenWindow = (windowName: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const closeWindow = (windowId: string) => {
|
const closeWindow = (windowId: string) => {
|
||||||
activeWindows.value = activeWindows.value.filter((window) => window.id !== windowId);
|
activeWindows.value = activeWindows.value.filter(
|
||||||
|
(window) => window.id !== windowId,
|
||||||
|
);
|
||||||
console.log("activeWindows.value", activeWindows.value);
|
console.log("activeWindows.value", activeWindows.value);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -171,25 +189,38 @@ const closeWindow = (windowId: string) => {
|
|||||||
>
|
>
|
||||||
<!--Menu container-->
|
<!--Menu container-->
|
||||||
<div class="flex flex-row g-2 text-gray-400 text-white z-9999">
|
<div class="flex flex-row g-2 text-gray-400 text-white z-9999">
|
||||||
<button @click="toggleMenu" class="w-8 h-8 text-white hover:text-blue-500 transition-all duration-100 flex flex-row">
|
<button
|
||||||
<ComputerDesktopIcon/>
|
@click="toggleMenu"
|
||||||
|
class="w-8 h-8 text-white hover:text-blue-500 transition-all duration-100 flex flex-row"
|
||||||
|
>
|
||||||
|
<ComputerDesktopIcon />
|
||||||
</button>
|
</button>
|
||||||
<span class="ml-1 mr-2 text-[20px]">|</span>
|
<span class="ml-1 mr-2 text-[20px]">|</span>
|
||||||
<!--navbar icons for min and max application window-->
|
<!--navbar icons for min and max application window-->
|
||||||
<button
|
<button
|
||||||
class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100"
|
class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100"
|
||||||
|
></button>
|
||||||
|
<div
|
||||||
|
v-for="item in currentNavBar"
|
||||||
|
:key="item.name"
|
||||||
|
class="flex flex-row items-center gap-x-2 hover:bg-gray-100 transition-all duration-100 px-4 py-2 cursor-pointer"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
@click="unMinWindow(item.windowAssociated)"
|
||||||
|
class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100"
|
||||||
>
|
>
|
||||||
</button>
|
|
||||||
<div v-for="item in currentNavBar" :key="item.name" class="flex flex-row items-center gap-x-2 hover:bg-gray-100 transition-all duration-100 px-4 py-2 cursor-pointer">
|
|
||||||
<button @click="unMinWindow(item.windowAssociated)" class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100">
|
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
<span v-if="item.flash" class="animate-ping absolute inline-flex h-3 w-3 rounded-full bg-red-400 opacity-75"></span>
|
<span
|
||||||
<span v-if="item.icon" :class="item.icon">
|
v-if="item.flash"
|
||||||
</span>
|
class="animate-ping absolute inline-flex h-3 w-3 rounded-full bg-red-400 opacity-75"
|
||||||
|
></span>
|
||||||
|
<span v-if="item.icon" :class="item.icon"> </span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center align-middle justify-center text-white">{{ currentDate }}</div>
|
<div class="text-center align-middle justify-center text-white">
|
||||||
|
{{ currentDate }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full h-[2.5em]"></div>
|
<div class="w-full h-[2.5em]"></div>
|
||||||
<!--Menu-->
|
<!--Menu-->
|
||||||
@ -197,9 +228,15 @@ const closeWindow = (windowId: string) => {
|
|||||||
enter-active-class="animate__animated animate__fadeInDown animate_fast03"
|
enter-active-class="animate__animated animate__fadeInDown animate_fast03"
|
||||||
leave-active-class="animate__animated animate__fadeOutUp animate_fast03"
|
leave-active-class="animate__animated animate__fadeOutUp animate_fast03"
|
||||||
>
|
>
|
||||||
<div class="m-2 p-2 bg-gray-800 shadow-lg w-fit rounded-[10px] v-9998" v-if="menuOpen">
|
<div
|
||||||
|
class="m-2 p-2 bg-gray-800 shadow-lg w-fit rounded-[10px] v-9998"
|
||||||
|
v-if="menuOpen"
|
||||||
|
>
|
||||||
<div v-for="item in menuItems" :key="item.name" class="">
|
<div v-for="item in menuItems" :key="item.name" class="">
|
||||||
<button @click="openWindow(item.windowName)" class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100">
|
<button
|
||||||
|
@click="openWindow(item.windowName)"
|
||||||
|
class="flex flex-row items-center gap-x-2 text-gray-400 hover:text-gray-600 transition-all duration-100"
|
||||||
|
>
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
<ChevronRightIcon class="w-4 h-4 justify-center align-center" />
|
<ChevronRightIcon class="w-4 h-4 justify-center align-center" />
|
||||||
</button>
|
</button>
|
||||||
@ -210,8 +247,7 @@ const closeWindow = (windowId: string) => {
|
|||||||
<div
|
<div
|
||||||
class="flex flex-col justify-center align-center text-center absolute w-full h-screen inset-x-0 inset-y-0 z-[-1]"
|
class="flex flex-col justify-center align-center text-center absolute w-full h-screen inset-x-0 inset-y-0 z-[-1]"
|
||||||
id="desktop"
|
id="desktop"
|
||||||
>
|
></div>
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<DraggableWindow
|
<DraggableWindow
|
||||||
v-for="window in activeWindows"
|
v-for="window in activeWindows"
|
||||||
@ -221,7 +257,10 @@ const closeWindow = (windowId: string) => {
|
|||||||
:width="window.width"
|
:width="window.width"
|
||||||
:height="window.height"
|
:height="window.height"
|
||||||
>
|
>
|
||||||
<Component :is="window.component" @error="console.error('Error:', $event)" />
|
<Component
|
||||||
|
:is="window.component"
|
||||||
|
@error="console.error('Error:', $event)"
|
||||||
|
/>
|
||||||
</DraggableWindow>
|
</DraggableWindow>
|
||||||
</div>
|
</div>
|
||||||
<!--Footer-->
|
<!--Footer-->
|
||||||
@ -231,8 +270,12 @@ const closeWindow = (windowId: string) => {
|
|||||||
<div class="">
|
<div class="">
|
||||||
<!--Lang-->
|
<!--Lang-->
|
||||||
<span>Lang: </span>
|
<span>Lang: </span>
|
||||||
<span class="text-lg">{{ t("localeflag") }}</span>
|
<span class="text-lg">{{ t("localeflag") }}</span
|
||||||
<button class="w-4 h-4 hover:text-blue-200 transition-all duration-100" @click="toggleLangMenu">
|
>
|
||||||
|
<button
|
||||||
|
class="w-4 h-4 hover:text-blue-200 transition-all duration-100"
|
||||||
|
@click="toggleLangMenu"
|
||||||
|
>
|
||||||
<LanguageIcon />
|
<LanguageIcon />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -245,8 +288,13 @@ const closeWindow = (windowId: string) => {
|
|||||||
<span class="text-sm">{{ new Date().getFullYear() }} © yh</span>
|
<span class="text-sm">{{ new Date().getFullYear() }} © yh</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
<button @click="openWindow('login')" class="w-8 h-8 text-gray-400 flex flex-row">
|
<button
|
||||||
<UserIcon class="w-8 h-8 text-gray-400 hover:text-blue-500 transition-all duration-100" />
|
@click="openWindow('login')"
|
||||||
|
class="w-8 h-8 text-gray-400 flex flex-row"
|
||||||
|
>
|
||||||
|
<UserIcon
|
||||||
|
class="w-8 h-8 text-gray-400 hover:text-blue-500 transition-all duration-100"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1 @@
|
|||||||
<template>
|
<template>目前沒有手機版本</template>
|
||||||
目前沒有手機版本
|
|
||||||
</template>
|
|
||||||
|
@ -1,25 +1,31 @@
|
|||||||
# Status
|
# Status
|
||||||
|
|
||||||
## setn.py
|
## setn.py
|
||||||
|
|
||||||
Working
|
Working
|
||||||
|
|
||||||
## tvbs.py
|
## tvbs.py
|
||||||
|
|
||||||
Working
|
Working
|
||||||
|
|
||||||
## taisounds.py
|
## taisounds.py
|
||||||
|
|
||||||
Working
|
Working
|
||||||
|
|
||||||
## cna.py
|
## cna.py
|
||||||
|
|
||||||
Broken
|
Broken
|
||||||
|
|
||||||
Error: `Error: 'utf-8' codec can't decode byte 0x83 in position 0: invalid start byte`
|
Error: `Error: 'utf-8' codec can't decode byte 0x83 in position 0: invalid start byte`
|
||||||
|
|
||||||
## chinatimes.py
|
## chinatimes.py
|
||||||
|
|
||||||
Broken
|
Broken
|
||||||
|
|
||||||
Error: `Error: 'utf-8' codec can't decode byte 0xa3 in position 0: invalid start byte`
|
Error: `Error: 'utf-8' codec can't decode byte 0xa3 in position 0: invalid start byte`
|
||||||
|
|
||||||
## twreporter.py
|
## twreporter.py
|
||||||
|
|
||||||
Broken
|
Broken
|
||||||
|
|
||||||
Error: `Error: 'utf-8' codec can't decode byte 0xc0 in position 2: invalid start byte`
|
Error: `Error: 'utf-8' codec can't decode byte 0xc0 in position 2: invalid start byte`
|
Loading…
x
Reference in New Issue
Block a user