feat: update README and various components for improved formatting and error handling; enhance login functionality with password encryption

This commit is contained in:
吳元皓 2025-05-12 10:17:21 +08:00
parent ee13960f0b
commit fd2ba525f9
12 changed files with 304 additions and 168 deletions

View File

@ -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.

View File

@ -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"
> >
@ -84,4 +84,4 @@ onUnmounted(() => {
<slot></slot> <slot></slot>
</div> </div>
</div> </div>
</template> </template>

View File

@ -62,4 +62,4 @@ const title = ref("Hot News");
</ul> </ul>
</div> </div>
</div> </div>
</template> </template>

View File

@ -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
<div class="text-xl mb-4 text-bold">Login / Register</div> class="flex flex-col items-center justify-center h-full"
>
<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"
Log In class="mb-2 p-2 border rounded"
</button> />
</form> <input
</div> type="password"
</template> 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
</button>
</div>
</div>
</template>

View File

@ -77,4 +77,4 @@ const toggleDropdown = () => {
</Transition> </Transition>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,7 +1,7 @@
version: '3.8' version: "3.8"
services: services:
newsanalyze-service: newsanalyze-service:
build: . build: .
ports: ports:
- "127.0.0.1:36694:3000" - "127.0.0.1:36694:3000"
@ -12,4 +12,4 @@ services:
test: ["CMD", "curl", "-f", "http://localhost:3000"] test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s interval: 30s
timeout: 10s timeout: 10s
retries: 3 retries: 3

View File

@ -1,13 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
const error = useError(); const error = useError();
const { t } = useI18n(); 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> >
</div> <span
<div class="h-screen"></div> class="text-7xl m-4 m-1 mb-0 text-center align-center justify-center"
</NuxtLayout> >{{ error.statusCode }}</span
</template> >
<span class="text-2xl text-center align-center justify-center">{{
error.statusMessage
}}</span>
</div>
<div class="h-screen"></div>
</NuxtLayout>
</template>

View File

@ -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();
@ -79,53 +83,68 @@ const openWindow = (windowName?: string) => {
router.push(localePath("/home")); router.push(localePath("/home"));
} }
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
class="absolute inset-x-0 flex flex-row px-2 py-1 bg-[#7D7C7C]/70 text-white justify-between align-center text-center z-50" class="absolute inset-x-0 flex flex-row px-2 py-1 bg-[#7D7C7C]/70 text-white justify-between align-center text-center z-50"
> >
<!--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,31 +152,40 @@ 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
<div v-for="item in menuItems" :key="item.name" class=""> class="m-2 p-2 bg-gray-800 shadow-lg w-fit rounded-[10px] v-9998"
<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"> v-if="menuOpen"
<span>{{ item.name }}</span> >
<ChevronRightIcon class="w-4 h-4 justify-center align-center" /> <div v-for="item in menuItems" :key="item.name" class="">
</button> <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>
<ChevronRightIcon class="w-4 h-4 justify-center align-center" />
</button>
</div>
</div> </div>
</div>
</Transition> </Transition>
<!--Main desktop contents--> <!--Main desktop contents-->
<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>&nbsp; <span class="text-lg">{{ t("localeflag") }}</span
<button class="w-4 h-4 hover:text-blue-200 transition-all duration-100" @click="toggleLangMenu"> >&nbsp;
<button
class="w-4 h-4 hover:text-blue-200 transition-all duration-100"
@click="toggleLangMenu"
>
<LanguageIcon /> <LanguageIcon />
</button> </button>
</div> </div>
@ -170,9 +198,14 @@ const toggleLangMenu = () => {
<span class="text-sm">{{ new Date().getFullYear() }} &copy yh</span> <span class="text-sm">{{ new Date().getFullYear() }} &copy 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')"
</button> 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>
</div> </div>
</div> </div>
</template> </template>

View File

@ -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"],

View File

@ -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();
@ -93,29 +96,29 @@ const openWindow = (windowName?: string) => {
if (windowName) findAndOpenWindow(windowName); if (windowName) findAndOpenWindow(windowName);
} }
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,27 +126,40 @@ 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(
if (newVal) { () => route.query.openapp,
openApp.value = true; (newVal) => {
openAppId.value = newVal; if (newVal) {
console.log("openAppId", openAppId.value); openApp.value = true;
router.replace({ openAppId.value = newVal;
path: route.path, console.log("openAppId", openAppId.value);
query: {}, router.replace({
}); path: route.path,
} 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>
@ -169,27 +187,40 @@ const closeWindow = (windowId: string) => {
<div <div
class="absolute inset-x-0 flex flex-row px-2 py-1 bg-[#7D7C7C]/70 text-white justify-between align-center text-center z-50" class="absolute inset-x-0 flex flex-row px-2 py-1 bg-[#7D7C7C]/70 text-white justify-between align-center text-center z-50"
> >
<!--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>
</button> <div
<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"> v-for="item in currentNavBar"
<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"> :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,23 +228,28 @@ 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
<div v-for="item in menuItems" :key="item.name" class=""> class="m-2 p-2 bg-gray-800 shadow-lg w-fit rounded-[10px] v-9998"
<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"> v-if="menuOpen"
<span>{{ item.name }}</span> >
<ChevronRightIcon class="w-4 h-4 justify-center align-center" /> <div v-for="item in menuItems" :key="item.name" class="">
</button> <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>
<ChevronRightIcon class="w-4 h-4 justify-center align-center" />
</button>
</div>
</div> </div>
</div>
</Transition> </Transition>
<!--Main desktop contents--> <!--Main desktop contents-->
<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"
:key="window.id" :key="window.id"
:title="window.title" :title="window.title"
@ -221,18 +257,25 @@ 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-->
<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>&nbsp; <span class="text-lg">{{ t("localeflag") }}</span
<button class="w-4 h-4 hover:text-blue-200 transition-all duration-100" @click="toggleLangMenu"> >&nbsp;
<button
class="w-4 h-4 hover:text-blue-200 transition-all duration-100"
@click="toggleLangMenu"
>
<LanguageIcon /> <LanguageIcon />
</button> </button>
</div> </div>
@ -245,9 +288,14 @@ const closeWindow = (windowId: string) => {
<span class="text-sm">{{ new Date().getFullYear() }} &copy yh</span> <span class="text-sm">{{ new Date().getFullYear() }} &copy 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')"
</button> 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>
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,3 +1 @@
<template> <template>目前沒有手機版本</template>
目前沒有手機版本
</template>

View File

@ -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`