This commit is contained in:
hxt
2025-04-17 22:38:34 +08:00
parent 878c05848d
commit 3541d17ef5
3 changed files with 304 additions and 1 deletions

View File

@@ -5,12 +5,86 @@ import Navbar from "./components/Navbar";
import Footer from "./components/Footer"; import Footer from "./components/Footer";
import Home from "./pages/Home"; import Home from "./pages/Home";
import Characters from "./pages/Characters"; import Characters from "./pages/Characters";
import CharacterDetail from "./pages/CharacterDetail";
import Builds from "./pages/Builds"; import Builds from "./pages/Builds";
import Battles from "./pages/Battles"; import Battles from "./pages/Battles";
import News from "./pages/News"; import News from "./pages/News";
import Community from "./pages/Community"; import Community from "./pages/Community";
import LoginModal from "./components/LoginModal"; import LoginModal from "./components/LoginModal";
// 导入类型定义
import { Skill, ImprintConcentration } from './pages/CharacterDetail';
// 示例数据
const mockCharacterData = {
id: 1,
name: "Boss Arunka",
stars: 5,
class: "Light Knight",
element: "light",
baseStats: {
attack: 821,
health: 6751,
defense: 648,
speed: 106
},
skills: [
{
name: "Road Sign Smash",
type: "Active" as const,
soulBurn: 1,
description: "Attacks the enemy with a road sign, with a 65% chance to decrease Defense for 2 turns.",
enhancements: [
{
stars: 2,
attack: "+5%",
effectiveness: "+10%"
},
{
stars: 3,
attack: "+10%",
effectiveness: "+15%"
}
]
},
{
name: "Vanguard of the Silent Expanse",
type: "Passive" as const,
description: "At the start of battle and at the end of the turn, when the caster is in the front row, increases Attack of all allies for 2 turns.",
enhancements: [
{
stars: 3,
attack: "+20%",
health: "+60"
}
]
}
] as Skill[],
imprintConcentration: [
{
type: "Attack Concentration",
values: [
{ rank: "SSS", value: "10.8%" },
{ rank: "SS", value: "9.0%" },
{ rank: "S", value: "7.2%" },
{ rank: "A", value: "5.4%" },
{ rank: "B", value: "3.6%" }
]
},
{
type: "Defense Concentration",
values: [
{ rank: "SSS", value: "18%" },
{ rank: "SS", value: "15%" },
{ rank: "S", value: "12%" },
{ rank: "A", value: "9%" },
{ rank: "B", value: "6%" }
]
}
] as ImprintConcentration[],
imageUrl: "https://readdy.ai/api/search-image?query=anime%20style%20game%20character%20portrait%20of%20a%20female%20warrior%20with%20pink%20hair%20and%20red%20eyes%2C%20fantasy%20RPG%20character%20icon%2C%20detailed%20face%2C%20high%20quality%2C%20digital%20art&width=100&height=100&seq=1&orientation=squarish"
};
const App: React.FC = () => { const App: React.FC = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); const [isLoginModalOpen, setIsLoginModalOpen] = useState(false);
@@ -51,9 +125,13 @@ const App: React.FC = () => {
<Routes> <Routes>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />
<Route path="/characters" element={<Characters />} /> <Route path="/characters" element={<Characters />} />
<Route
path="/character/:id"
element={<CharacterDetail character={mockCharacterData} />}
/>
<Route path="/news" element={<News />} />
<Route path="/builds" element={<Builds />} /> <Route path="/builds" element={<Builds />} />
<Route path="/battles" element={<Battles />} /> <Route path="/battles" element={<Battles />} />
<Route path="/news" element={<News />} />
<Route path="/community" element={<Community />} /> <Route path="/community" element={<Community />} />
</Routes> </Routes>
</main> </main>

View File

@@ -0,0 +1,217 @@
import React from 'react';
export interface Skill {
name: string;
type: 'None' | 'Passive' | 'Active';
soulBurn?: number;
description: string;
enhancements?: {
stars: number;
attack?: string;
health?: string;
defense?: string;
effectiveness?: string;
}[];
}
export interface ImprintConcentration {
type: string;
values: {
rank: string;
value: string;
}[];
}
interface CharacterDetailProps {
character: {
id: number;
name: string;
stars: number;
class: string;
element: string;
baseStats: {
attack: number;
health: number;
defense: number;
speed: number;
};
skills: Skill[];
imprintConcentration: ImprintConcentration[];
imageUrl: string;
};
}
const CharacterDetail: React.FC<CharacterDetailProps> = ({ character }) => {
const renderStars = (count: number) => {
return Array(count)
.fill(0)
.map((_, index) => (
<i key={index} className="text-yellow-400 text-lg"></i>
));
};
const renderSkillType = (type: string) => {
switch (type) {
case 'None':
return <span className="bg-green-600 text-white text-xs px-2 py-1 rounded">None</span>;
case 'Passive':
return <span className="bg-green-600 text-white text-xs px-2 py-1 rounded">Passive</span>;
case 'Active':
return <span className="bg-blue-600 text-white text-xs px-2 py-1 rounded">Active</span>;
default:
return null;
}
};
const renderSoulBurn = (souls?: number) => {
if (!souls) return null;
return (
<span className="bg-teal-600 text-white text-xs px-2 py-1 rounded ml-2">
+{souls} souls
</span>
);
};
return (
<div className="min-h-screen bg-[#1A1412] text-white font-sans">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
{/* 角色基本信息 */}
<div className="bg-gradient-to-br from-[#2A211E] to-[#1A1412] rounded-lg p-6 mb-8 border border-[#C17F59]/30">
<div className="flex items-start space-x-6">
<div className="w-32 h-32 rounded-full overflow-hidden border-4 border-[#C17F59]">
<img
src={character.imageUrl}
alt={character.name}
className="w-full h-full object-cover"
/>
</div>
<div>
<h1 className="text-3xl font-bold text-[#E6B17E] mb-2">
{character.name}
</h1>
<div className="flex items-center space-x-4 mb-4">
<div className="flex">
{renderStars(character.stars)}
</div>
<span className="text-[#9B8579]">{character.class}</span>
</div>
</div>
</div>
</div>
{/* 基础属性 */}
<div className="bg-gradient-to-br from-[#2A211E] to-[#1A1412] rounded-lg p-6 mb-8 border border-[#C17F59]/30">
<h2 className="text-xl font-bold text-[#E6B17E] mb-4">Base Stats</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="flex items-center space-x-2">
<i className="fas fa-sword text-[#C17F59]"></i>
<span>Attack: {character.baseStats.attack}</span>
</div>
<div className="flex items-center space-x-2">
<i className="fas fa-heart text-[#C17F59]"></i>
<span>Health: {character.baseStats.health}</span>
</div>
<div className="flex items-center space-x-2">
<i className="fas fa-shield-alt text-[#C17F59]"></i>
<span>Defense: {character.baseStats.defense}</span>
</div>
<div className="flex items-center space-x-2">
<i className="fas fa-wind text-[#C17F59]"></i>
<span>Speed: {character.baseStats.speed}</span>
</div>
</div>
</div>
{/* 技能列表 */}
<div className="space-y-6">
{character.skills.map((skill, index) => (
<div
key={index}
className="bg-gradient-to-br from-[#2A211E] to-[#1A1412] rounded-lg p-6 border border-[#C17F59]/30"
>
<div className="flex items-center justify-between mb-4">
<h3 className="text-xl font-bold text-[#E6B17E]">
{skill.name}
</h3>
<div className="flex items-center">
{renderSkillType(skill.type)}
{renderSoulBurn(skill.soulBurn)}
</div>
</div>
<p className="text-[#9B8579] mb-4">{skill.description}</p>
{skill.enhancements && skill.enhancements.length > 0 && (
<div className="mt-4">
<h4 className="text-lg font-semibold text-[#E6B17E] mb-2">
Skill Enhancements
</h4>
<div className="space-y-2">
{skill.enhancements.map((enhancement, idx) => (
<div
key={idx}
className="flex items-center space-x-4 text-sm"
>
<div className="flex">
{renderStars(enhancement.stars)}
</div>
<div className="flex flex-wrap gap-2">
{enhancement.attack && (
<span className="text-[#9B8579]">
Attack {enhancement.attack}
</span>
)}
{enhancement.health && (
<span className="text-[#9B8579]">
Health {enhancement.health}
</span>
)}
{enhancement.defense && (
<span className="text-[#9B8579]">
Defense {enhancement.defense}
</span>
)}
{enhancement.effectiveness && (
<span className="text-[#9B8579]">
Effectiveness {enhancement.effectiveness}
</span>
)}
</div>
</div>
))}
</div>
</div>
)}
</div>
))}
</div>
{/* 印记集中 */}
<div className="bg-gradient-to-br from-[#2A211E] to-[#1A1412] rounded-lg p-6 mt-8 border border-[#C17F59]/30">
<h2 className="text-xl font-bold text-[#E6B17E] mb-4">
Imprint Concentration
</h2>
{character.imprintConcentration.map((imprint, index) => (
<div key={index} className="mb-4">
<h3 className="text-lg font-semibold text-[#C17F59] mb-2">
{imprint.type}
</h3>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{imprint.values.map((value, idx) => (
<div
key={idx}
className="flex items-center justify-between bg-[#1A1412] p-2 rounded border border-[#C17F59]/30"
>
<span className="text-[#E6B17E]">{value.rank}</span>
<span className="text-[#9B8579]">{value.value}</span>
</div>
))}
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default CharacterDetail;

View File

@@ -1,6 +1,7 @@
// The exported code uses Tailwind CSS. Install Tailwind CSS in your dev environment to ensure all styles work. // The exported code uses Tailwind CSS. Install Tailwind CSS in your dev environment to ensure all styles work.
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import { useNavigate } from 'react-router-dom';
const Characters: React.FC = () => { const Characters: React.FC = () => {
const [selectedStars, setSelectedStars] = useState<string>("All Stars"); const [selectedStars, setSelectedStars] = useState<string>("All Stars");
@@ -40,6 +41,8 @@ const Characters: React.FC = () => {
}; };
}, []); }, []);
const navigate = useNavigate();
const heroes = [ const heroes = [
{ {
id: 1, id: 1,
@@ -606,6 +609,10 @@ const Characters: React.FC = () => {
return true; return true;
}); });
const handleCharacterClick = (heroId: number) => {
navigate(`/character/${heroId}`);
};
return ( return (
<div className="min-h-screen bg-[#1A1412] text-white font-sans relative"> <div className="min-h-screen bg-[#1A1412] text-white font-sans relative">
{/* 背景装饰 */} {/* 背景装饰 */}
@@ -945,6 +952,7 @@ const Characters: React.FC = () => {
<div <div
key={hero.id} key={hero.id}
className="bg-gradient-to-br from-[#2A211E] to-[#1A1412] rounded-lg overflow-hidden border border-[#C17F59]/30 hover:border-[#C17F59] transition-all duration-300 transform hover:-translate-y-1 hover:shadow-xl hover:shadow-[#C17F59]/10 cursor-pointer backdrop-blur-sm" className="bg-gradient-to-br from-[#2A211E] to-[#1A1412] rounded-lg overflow-hidden border border-[#C17F59]/30 hover:border-[#C17F59] transition-all duration-300 transform hover:-translate-y-1 hover:shadow-xl hover:shadow-[#C17F59]/10 cursor-pointer backdrop-blur-sm"
onClick={() => handleCharacterClick(hero.id)}
> >
<div className="relative h-48 overflow-hidden"> <div className="relative h-48 overflow-hidden">
<img <img