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
|
||||
|
||||
- puter.com
|
||||
- Perplexity
|
||||
- Ground.news
|
||||
@ -86,4 +87,5 @@ App Design: [Freeform](https://www.icloud.com/freeform/026AxB798cViZ9jJ2DkNsXUCQ
|
||||
7. Open `http://localhost:3000` in your browser.
|
||||
|
||||
### 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.
|
@ -1,53 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
initialX?: number
|
||||
initialY?: number
|
||||
width?: string
|
||||
height?: string
|
||||
}>()
|
||||
title: string;
|
||||
initialX?: number;
|
||||
initialY?: number;
|
||||
width?: string;
|
||||
height?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
const emit = defineEmits(["close"]);
|
||||
|
||||
const isDragging = ref(false)
|
||||
const isDragging = ref(false);
|
||||
const position = ref({
|
||||
x: props.initialX || Math.floor(window.innerWidth / 2) - Math.random() * 200,
|
||||
y: props.initialY || Math.floor(window.innerHeight / 2) - Math.random() * 10
|
||||
})
|
||||
const offset = ref({ x: 0, y: 0 })
|
||||
y: props.initialY || Math.floor(window.innerHeight / 2) - Math.random() * 10,
|
||||
});
|
||||
const offset = ref({ x: 0, y: 0 });
|
||||
|
||||
const startDrag = (e: MouseEvent) => {
|
||||
isDragging.value = true
|
||||
isDragging.value = true;
|
||||
offset.value = {
|
||||
x: e.clientX - position.value.x,
|
||||
y: e.clientY - position.value.y
|
||||
}
|
||||
}
|
||||
y: e.clientY - position.value.y,
|
||||
};
|
||||
};
|
||||
|
||||
const doDrag = (e: MouseEvent) => {
|
||||
if (isDragging.value) {
|
||||
position.value = {
|
||||
x: e.clientX - offset.value.x,
|
||||
y: e.clientY - offset.value.y
|
||||
y: e.clientY - offset.value.y,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const stopDrag = () => {
|
||||
isDragging.value = false
|
||||
}
|
||||
isDragging.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('mousemove', doDrag)
|
||||
document.addEventListener('mouseup', stopDrag)
|
||||
})
|
||||
document.addEventListener("mousemove", doDrag);
|
||||
document.addEventListener("mouseup", stopDrag);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('mousemove', doDrag)
|
||||
document.removeEventListener('mouseup', stopDrag)
|
||||
})
|
||||
document.removeEventListener("mousemove", doDrag);
|
||||
document.removeEventListener("mouseup", stopDrag);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -56,7 +56,7 @@ onUnmounted(() => {
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
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"
|
||||
>
|
||||
|
@ -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>
|
||||
<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>
|
||||
|
||||
<input type="text" 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">
|
||||
<input
|
||||
type="text"
|
||||
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
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -1,4 +1,4 @@
|
||||
version: '3.8'
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
newsanalyze-service:
|
||||
|
13
error.vue
13
error.vue
@ -4,9 +4,16 @@ const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<NuxtLayout>
|
||||
<div class="flex flex-col justify-center align-center text-center absolute inset-0 h-screen">
|
||||
<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
|
||||
class="flex flex-col justify-center align-center text-center absolute inset-0 h-screen"
|
||||
>
|
||||
<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 class="h-screen"></div>
|
||||
</NuxtLayout>
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
// No layout
|
||||
|
||||
@ -28,7 +27,12 @@ import ProgressComponent from "~/components/ui/progress/Progress.vue";
|
||||
import HoverCardComponent from "~/components/ui/hover-card/HoverCard.vue";
|
||||
|
||||
// Icons
|
||||
import { ComputerDesktopIcon, UserIcon, LanguageIcon, ChevronRightIcon } from "@heroicons/vue/24/outline";
|
||||
import {
|
||||
ComputerDesktopIcon,
|
||||
UserIcon,
|
||||
LanguageIcon,
|
||||
ChevronRightIcon,
|
||||
} from "@heroicons/vue/24/outline";
|
||||
|
||||
// i18n
|
||||
const { t, locale, locales } = useI18n();
|
||||
@ -80,28 +84,28 @@ const openWindow = (windowName?: string) => {
|
||||
}
|
||||
console.log(windowName);
|
||||
menuOpen.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const unMinWindow = (windowName?: string) => {
|
||||
console.log(windowName);
|
||||
}
|
||||
};
|
||||
|
||||
// menus
|
||||
const menuItems = [
|
||||
{ name: "Hot News", windowName: "hotnews"} ,
|
||||
{ name: "News", windowName: "news"},
|
||||
{ name: "Sources", windowName: "sources"},
|
||||
{ name: 'About This Website', windowName: "about"},
|
||||
{ name: 'Settings', windowName: "settings"},
|
||||
{ name: 'Leave', windowName: "leave"},
|
||||
]
|
||||
{ name: "Hot News", windowName: "hotnews" },
|
||||
{ name: "News", windowName: "news" },
|
||||
{ name: "Sources", windowName: "sources" },
|
||||
{ name: "About This Website", windowName: "about" },
|
||||
{ name: "Settings", windowName: "settings" },
|
||||
{ name: "Leave", windowName: "leave" },
|
||||
];
|
||||
const toggleMenu = () => {
|
||||
menuOpen.value = !menuOpen.value
|
||||
}
|
||||
menuOpen.value = !menuOpen.value;
|
||||
};
|
||||
// Lang Menu
|
||||
const toggleLangMenu = () => {
|
||||
langMenuOpen.value = !langMenuOpen.value
|
||||
}
|
||||
langMenuOpen.value = !langMenuOpen.value;
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
@ -109,23 +113,38 @@ const toggleLangMenu = () => {
|
||||
>
|
||||
<!--Menu container-->
|
||||
<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">
|
||||
<ComputerDesktopIcon/>
|
||||
<button
|
||||
@click="toggleMenu"
|
||||
class="w-8 h-8 text-white hover:text-blue-500 transition-all duration-100 flex flex-row"
|
||||
>
|
||||
<ComputerDesktopIcon />
|
||||
</button>
|
||||
<span class="ml-1 mr-2 text-[20px]">|</span>
|
||||
<!--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>
|
||||
<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
|
||||
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 v-if="item.flash" 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>
|
||||
<span
|
||||
v-if="item.flash"
|
||||
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>
|
||||
</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 class="w-full h-[2.5em]"></div>
|
||||
<!--Menu-->
|
||||
@ -133,9 +152,15 @@ const toggleLangMenu = () => {
|
||||
enter-active-class="animate__animated animate__fadeInDown 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="">
|
||||
<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>
|
||||
<ChevronRightIcon class="w-4 h-4 justify-center align-center" />
|
||||
</button>
|
||||
@ -146,18 +171,21 @@ const toggleLangMenu = () => {
|
||||
<div
|
||||
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"
|
||||
>
|
||||
</div>
|
||||
<slot/>
|
||||
<!--Footer-->
|
||||
></div>
|
||||
<slot />
|
||||
<!--Footer-->
|
||||
<div
|
||||
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="">
|
||||
<!--Lang-->
|
||||
<span>Lang: </span>
|
||||
<span class="text-lg">{{ t("localeflag") }}</span>
|
||||
<button class="w-4 h-4 hover:text-blue-200 transition-all duration-100" @click="toggleLangMenu">
|
||||
<span class="text-lg">{{ t("localeflag") }}</span
|
||||
>
|
||||
<button
|
||||
class="w-4 h-4 hover:text-blue-200 transition-all duration-100"
|
||||
@click="toggleLangMenu"
|
||||
>
|
||||
<LanguageIcon />
|
||||
</button>
|
||||
</div>
|
||||
@ -170,8 +198,13 @@ const toggleLangMenu = () => {
|
||||
<span class="text-sm">{{ new Date().getFullYear() }} © yh</span>
|
||||
</div>
|
||||
<div class="">
|
||||
<button @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
|
||||
@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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,9 +9,15 @@ export default defineNuxtConfig({
|
||||
"/go/**": { ssr: true },
|
||||
"/find/**": { ssr: true },
|
||||
// Send ZIP bombs to troll bots
|
||||
"/wp-admin/**": { redirect: "https://s3.yhw.tw/data/def-zip-bomb/wp-admin.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" },
|
||||
"/wp-admin/**": {
|
||||
redirect: "https://s3.yhw.tw/data/def-zip-bomb/wp-admin.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"],
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
// No layout
|
||||
definePageMeta({
|
||||
@ -38,9 +37,13 @@ import DialogComponent from "~/components/ui/dialog/Dialog.vue";
|
||||
import ProgressComponent from "~/components/ui/progress/Progress.vue";
|
||||
import HoverCardComponent from "~/components/ui/hover-card/HoverCard.vue";
|
||||
|
||||
|
||||
// Icons
|
||||
import { ComputerDesktopIcon, UserIcon, LanguageIcon, ChevronRightIcon } from "@heroicons/vue/24/outline";
|
||||
import {
|
||||
ComputerDesktopIcon,
|
||||
UserIcon,
|
||||
LanguageIcon,
|
||||
ChevronRightIcon,
|
||||
} from "@heroicons/vue/24/outline";
|
||||
|
||||
// i18n
|
||||
const { t, locale, locales } = useI18n();
|
||||
@ -94,28 +97,28 @@ const openWindow = (windowName?: string) => {
|
||||
}
|
||||
console.log(windowName);
|
||||
menuOpen.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const unMinWindow = (windowName?: string) => {
|
||||
console.log(windowName);
|
||||
}
|
||||
};
|
||||
|
||||
// menus
|
||||
const menuItems = [
|
||||
{ name: "Hot News", windowName: "hotnews"} ,
|
||||
{ name: "News", windowName: "news"},
|
||||
{ name: "Sources", windowName: "sources"},
|
||||
{ name: 'About This Website', windowName: "about"},
|
||||
{ name: 'Settings', windowName: "settings"},
|
||||
{ name: 'Leave', windowName: "leave"},
|
||||
]
|
||||
{ name: "Hot News", windowName: "hotnews" },
|
||||
{ name: "News", windowName: "news" },
|
||||
{ name: "Sources", windowName: "sources" },
|
||||
{ name: "About This Website", windowName: "about" },
|
||||
{ name: "Settings", windowName: "settings" },
|
||||
{ name: "Leave", windowName: "leave" },
|
||||
];
|
||||
const toggleMenu = () => {
|
||||
menuOpen.value = !menuOpen.value
|
||||
}
|
||||
menuOpen.value = !menuOpen.value;
|
||||
};
|
||||
// Lang Menu
|
||||
const toggleLangMenu = () => {
|
||||
langMenuOpen.value = !langMenuOpen.value
|
||||
}
|
||||
langMenuOpen.value = !langMenuOpen.value;
|
||||
};
|
||||
|
||||
// values
|
||||
const activeWindows = ref<associAppWindowInterface>([]);
|
||||
@ -123,7 +126,9 @@ const activeWindows = ref<associAppWindowInterface>([]);
|
||||
// ?opemapp= component
|
||||
const openApp = ref(false);
|
||||
const openAppId = ref();
|
||||
watch(() => route.query.openapp, (newVal) => {
|
||||
watch(
|
||||
() => route.query.openapp,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
openApp.value = true;
|
||||
openAppId.value = newVal;
|
||||
@ -133,17 +138,28 @@ watch(() => route.query.openapp, (newVal) => {
|
||||
query: {},
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
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 },
|
||||
]
|
||||
];
|
||||
|
||||
const findAndOpenWindow = (windowName: string) => {
|
||||
const app = associAppWindow.find((app) => app.name === windowName);
|
||||
// Prevent dual logins
|
||||
if (windowName === "login" && activeWindows.value.some((window) => window.name === "login")) {
|
||||
if (
|
||||
windowName === "login" &&
|
||||
activeWindows.value.some((window) => window.name === "login")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (app) {
|
||||
@ -161,7 +177,9 @@ const findAndOpenWindow = (windowName: 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);
|
||||
};
|
||||
</script>
|
||||
@ -171,25 +189,38 @@ const closeWindow = (windowId: string) => {
|
||||
>
|
||||
<!--Menu container-->
|
||||
<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">
|
||||
<ComputerDesktopIcon/>
|
||||
<button
|
||||
@click="toggleMenu"
|
||||
class="w-8 h-8 text-white hover:text-blue-500 transition-all duration-100 flex flex-row"
|
||||
>
|
||||
<ComputerDesktopIcon />
|
||||
</button>
|
||||
<span class="ml-1 mr-2 text-[20px]">|</span>
|
||||
<!--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>
|
||||
<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 v-if="item.flash" 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>
|
||||
<span
|
||||
v-if="item.flash"
|
||||
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>
|
||||
</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 class="w-full h-[2.5em]"></div>
|
||||
<!--Menu-->
|
||||
@ -197,9 +228,15 @@ const closeWindow = (windowId: string) => {
|
||||
enter-active-class="animate__animated animate__fadeInDown 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="">
|
||||
<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>
|
||||
<ChevronRightIcon class="w-4 h-4 justify-center align-center" />
|
||||
</button>
|
||||
@ -210,8 +247,7 @@ const closeWindow = (windowId: string) => {
|
||||
<div
|
||||
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"
|
||||
>
|
||||
</div>
|
||||
></div>
|
||||
<div>
|
||||
<DraggableWindow
|
||||
v-for="window in activeWindows"
|
||||
@ -221,7 +257,10 @@ const closeWindow = (windowId: string) => {
|
||||
:width="window.width"
|
||||
:height="window.height"
|
||||
>
|
||||
<Component :is="window.component" @error="console.error('Error:', $event)" />
|
||||
<Component
|
||||
:is="window.component"
|
||||
@error="console.error('Error:', $event)"
|
||||
/>
|
||||
</DraggableWindow>
|
||||
</div>
|
||||
<!--Footer-->
|
||||
@ -231,8 +270,12 @@ const closeWindow = (windowId: string) => {
|
||||
<div class="">
|
||||
<!--Lang-->
|
||||
<span>Lang: </span>
|
||||
<span class="text-lg">{{ t("localeflag") }}</span>
|
||||
<button class="w-4 h-4 hover:text-blue-200 transition-all duration-100" @click="toggleLangMenu">
|
||||
<span class="text-lg">{{ t("localeflag") }}</span
|
||||
>
|
||||
<button
|
||||
class="w-4 h-4 hover:text-blue-200 transition-all duration-100"
|
||||
@click="toggleLangMenu"
|
||||
>
|
||||
<LanguageIcon />
|
||||
</button>
|
||||
</div>
|
||||
@ -245,8 +288,13 @@ const closeWindow = (windowId: string) => {
|
||||
<span class="text-sm">{{ new Date().getFullYear() }} © yh</span>
|
||||
</div>
|
||||
<div class="">
|
||||
<button @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
|
||||
@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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,3 +1 @@
|
||||
<template>
|
||||
目前沒有手機版本
|
||||
</template>
|
||||
<template>目前沒有手機版本</template>
|
||||
|
@ -1,25 +1,31 @@
|
||||
# Status
|
||||
|
||||
## setn.py
|
||||
|
||||
Working
|
||||
|
||||
## tvbs.py
|
||||
|
||||
Working
|
||||
|
||||
## taisounds.py
|
||||
|
||||
Working
|
||||
|
||||
## cna.py
|
||||
|
||||
Broken
|
||||
|
||||
Error: `Error: 'utf-8' codec can't decode byte 0x83 in position 0: invalid start byte`
|
||||
|
||||
## chinatimes.py
|
||||
|
||||
Broken
|
||||
|
||||
Error: `Error: 'utf-8' codec can't decode byte 0xa3 in position 0: invalid start byte`
|
||||
|
||||
## twreporter.py
|
||||
|
||||
Broken
|
||||
|
||||
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