import { Layout } from "@/components/Layout"; import type { Route } from "./+types/home"; import { useEffect, useState } from "react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { treaty } from "@elysiajs/eden"; import type { App } from "@elysia/src"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Skeleton } from "@/components/ui/skeleton"; import { formatDateTime } from "@/components/SearchResults"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Card } from "@/components/ui/card"; import { Progress } from "@/components/ui/progress"; import { addHoursToNow, formatHours } from "./song/[id]/info"; // @ts-ignore idk const app = treaty(import.meta.env.VITE_API_URL!); type CloseMilestoneInfo = Awaited["get"]>>["data"]; type CloseMilestoneError = Awaited["get"]>>["error"]; export function meta({}: Route.MetaArgs) { return [{ title: "中V档案馆" }]; } type MilestoneType = "dendou" | "densetsu" | "shinwa"; const milestoneConfig = { dendou: { name: "殿堂", range: [90000, 99999], target: 100000 }, densetsu: { name: "传说", range: [900000, 999999], target: 1000000 }, shinwa: { name: "神话", range: [5000000, 9999999], target: 10000000 }, }; export default function Home() { const [input, setInput] = useState(""); const [milestoneType, setMilestoneType] = useState("shinwa"); const [closeMilestoneInfo, setCloseMilestoneInfo] = useState(); const [closeMilestoneError, setCloseMilestoneError] = useState(); const [isLoading, setIsLoading] = useState(false); const fetchMilestoneData = async (type: MilestoneType) => { setIsLoading(true); setCloseMilestoneError(undefined); const { data, error } = await app.songs["close-milestone"]({ type }).get(); if (error) { setCloseMilestoneError(error); } else { setCloseMilestoneInfo(data); } setIsLoading(false); }; useEffect(() => { fetchMilestoneData(milestoneType); }, [milestoneType]); const MilestoneVideoCard = ({ video }: { video: NonNullable[number] }) => { const config = milestoneConfig[milestoneType]; const remainingViews = config.target - video.eta.currentViews; const progressPercentage = (video.eta.currentViews / config.target) * 100; return (
{video.bilibili_metadata.coverUrl && ( 视频封面 )}

{video.bilibili_metadata.title}

当前播放: {video.eta.currentViews.toLocaleString()} 目标: {config.target.toLocaleString()}

剩余播放: {remainingViews.toLocaleString()}

预计达成: {formatHours(video.eta.eta)}

播放速度: {Math.round(video.eta.speed)}/小时

达成时间: {addHoursToNow(video.eta.eta)}

{video.bilibili_metadata.publishedAt && ( 发布于 {formatDateTime(new Date(video.bilibili_metadata.publishedAt))} )} 观看视频 查看详情
); }; const MilestoneVideos = () => { if (isLoading) { return (
{[1, 2, 3].map((i) => (
))}
); } if (closeMilestoneError) { return (

加载失败: {closeMilestoneError.value?.message || "未知错误"}

); } if (!closeMilestoneInfo || closeMilestoneInfo.length === 0) { return (

暂无接近{milestoneConfig[milestoneType].name}的视频

); } return (

找到 {closeMilestoneInfo.length} 个接近{milestoneConfig[milestoneType].name}的视频

{closeMilestoneInfo.map((video) => ( ))}
); }; return (

小工具

setInput(e.target.value)} />

即将达成成就

播放量在 {milestoneConfig[milestoneType].range[0].toLocaleString()} -{" "} {milestoneConfig[milestoneType].range[1].toLocaleString()} 之间,即将达成 {milestoneConfig[milestoneType].name}
); }