init
This commit is contained in:
80
src/App.tsx
80
src/App.tsx
@@ -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>
|
||||||
|
|||||||
217
src/pages/CharacterDetail.tsx
Normal file
217
src/pages/CharacterDetail.tsx
Normal 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;
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user