✨Feat: 完成前端郭德纲教学卡片

This commit is contained in:
2026-03-27 20:30:49 +08:00
parent 541ca96fc6
commit c0414e7548
5 changed files with 124 additions and 2 deletions
+3 -1
View File
@@ -1,14 +1,16 @@
import Card from "./components/Card";
import Guodegang from "./components/Guodegang";
import { Surface } from "@heroui/react";
function App() {
return (
<>
<Surface
className="flex min-w-[320px] flex-col gap-3 rounded-3xl p-6"
className="flex min-w-[320px] flex-row gap-4 rounded-3xl p-6 flex-wrap justify-center"
variant="default"
>
<Card topic="React Hooks" />
<Guodegang topic="React Hooks" />
</Surface>
</>
);
+84
View File
@@ -0,0 +1,84 @@
import { Card } from "@heroui/react";
import { useEffect, useState } from "react";
interface GuodegangData {
name: string;
content: string;
}
interface Props {
topic: string;
}
export default function Guodegang({ topic }: Props) {
const [data, setData] = useState<GuodegangData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(
`http://localhost:8080/guodegang/${encodeURIComponent(topic)}`,
);
if (!response.ok) {
throw new Error("API Error");
}
const result = await response.json();
setData(result.message);
setError(null);
} catch (error) {
console.error("Failed to fetch data: ", error);
setError("Failed to load data. Please try again.");
} finally {
setLoading(false);
}
};
fetchData();
}, [topic]);
if (loading) {
return (
<div className="w-full p-4 sm:p-6 md:max-w-2xl lg:max-w-4xl mx-auto">
<Card className="relative w-full max-w-2xl mx-auto bg-white shadow-xl rounded-2xl overflow-hidden border border-gray-100 p-8">
<div className="text-center text-gray-600">Loading...</div>
</Card>
</div>
);
}
if (error) {
return (
<div className="w-full p-4 sm:p-6 md:max-w-2xl lg:max-w-4xl mx-auto">
<Card className="relative w-full max-w-2xl mx-auto bg-white shadow-xl rounded-2xl overflow-hidden border border-gray-100 p-8">
<div className="text-center text-red-600">{error}</div>
</Card>
</div>
);
}
return (
<div className="w-full p-4 sm:p-6 md:max-w-2xl lg:max-w-4xl mx-auto">
<Card className="relative w-full max-w-2xl mx-auto bg-white shadow-xl rounded-2xl overflow-hidden border border-gray-100">
<div className="absolute upper-0 right-0 -mr-2 -mb-2 pointer-events-none select-none">
<span className="text-[10rem] opacity-80 blur-none">🎤</span>
</div>
<div className="relative z-5">
<div className="px-6 pt-6 pb-4">
<Card.Title className="text-2xl font-bold text-gray-800 mb-2">
{data!.name}
</Card.Title>
<div className="h-1 w-20 bg-gradient-to-r from-yellow-500 to-orange-500 rounded"></div>
</div>
<Card.Description className="px-6 pb-6">
<p className="text-gray-700 leading-relaxed whitespace-pre-line">
{data!.content}
</p>
</Card.Description>
</div>
</Card>
</div>
);
}