From 85c381ab506fa6d9adcfda363a24e035a7856813 Mon Sep 17 00:00:00 2001 From: hxt Date: Tue, 22 Apr 2025 23:02:07 +0800 Subject: [PATCH] init --- src/App.tsx | 2 + src/api/gvg.ts | 42 +++++ src/api/gvg/index.ts | 31 ++++ src/components/Navbar.tsx | 20 ++ src/pages/Lineup.tsx | 377 ++++++++++++++++++++++++++++++++++++++ src/utils/request.ts | 42 +++++ 6 files changed, 514 insertions(+) create mode 100644 src/api/gvg.ts create mode 100644 src/api/gvg/index.ts create mode 100644 src/pages/Lineup.tsx create mode 100644 src/utils/request.ts diff --git a/src/App.tsx b/src/App.tsx index 8a096e3..4153a6d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,6 +11,7 @@ import Battles from "./pages/Battles"; import News from "./pages/News"; import Community from "./pages/Community"; import LoginModal from "./components/LoginModal"; +import Lineup from "./pages/Lineup"; // 导入类型定义 import { Skill, ImprintConcentration } from './pages/CharacterDetail'; @@ -132,6 +133,7 @@ const App: React.FC = () => { } /> } /> } /> + } /> } /> diff --git a/src/api/gvg.ts b/src/api/gvg.ts new file mode 100644 index 0000000..be3be96 --- /dev/null +++ b/src/api/gvg.ts @@ -0,0 +1,42 @@ +import { request } from '@/utils/request' + +// 查询参数接口 +export interface GvgTeamQueryParams { + defenseHeroes?: string[] + attackHeroes?: string[] + equipmentInfo?: string + artifacts?: string + battleStrategy?: string + pageNum?: number + pageSize?: number +} + +// 阵容数据接口 +export interface GvgTeam { + id: number + defenseHeroes: string[] + attackHeroes: string[] + equipmentInfo: string + artifacts: string + battleStrategy: string + prerequisites: string + importantNotes: string + createTime: string + updateTime: string +} + +// 分页响应接口 +export interface PageResult { + total: number + list: T[] +} + +// 查询 GVG 阵容列表 +export const getGvgTeamList = (params: GvgTeamQueryParams) => { + return request.get>('/api/gvg/team/list', { params }) +} + +// 获取 GVG 阵容详情 +export const getGvgTeamDetail = (id: number) => { + return request.get(`/api/gvg/team/${id}`) +} \ No newline at end of file diff --git a/src/api/gvg/index.ts b/src/api/gvg/index.ts new file mode 100644 index 0000000..53e4c0d --- /dev/null +++ b/src/api/gvg/index.ts @@ -0,0 +1,31 @@ +import { request } from '@/utils/request' + +export interface GvgTeam { + id: number + defenseHeroes: string[] + attackHeroes: string[] + equipmentInfo: string + artifacts: string + battleStrategy: string + prerequisites: string + importantNotes: string + createTime: string +} + +export interface GvgTeamQueryParams { + pageNum?: number + pageSize?: number + defenseHeroes?: string + attackHeroes?: string + equipmentInfo?: string +} + +export interface PageResult { + total: number + list: T[] +} + +// Get GVG team list +export const getGvgTeamList = (params: GvgTeamQueryParams) => { + return request.get>('/epic/hero/team/list', { params }) +} \ No newline at end of file diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 2d473ed..54136e3 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -61,6 +61,16 @@ const Navbar: React.FC = ({ onLoginClick }) => { > 对战信息 + + 对战阵容 + = ({ onLoginClick }) => { > 对战信息 + + 对战阵容 + { + const [selectedDifficulty, setSelectedDifficulty] = useState("All"); + const [selectedTags, setSelectedTags] = useState([]); + const [searchTerm, setSearchTerm] = useState(""); + const [lineups, setLineups] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [pageNum, setPageNum] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [total, setTotal] = useState(0); + const navigate = useNavigate(); + + // Fetch data from API + useEffect(() => { + const fetchLineups = async () => { + setLoading(true); + try { + const params: GvgTeamQueryParams = { + pageNum, + pageSize, + // Add other filters as needed + }; + + const response = await getGvgTeamList(params); + setLineups(response.list); + setTotal(response.total); + setError(null); + } catch (err) { + console.error('Failed to fetch lineups:', err); + setError('获取阵容数据失败,请稍后再试'); + // Use mock data as fallback + setLineups(mockLineups); + } finally { + setLoading(false); + } + }; + + fetchLineups(); + }, [pageNum, pageSize]); + + // Mock data for fallback + const mockLineups: GvgTeam[] = [ + { + id: 1, + defenseHeroes: ["暗影刺客", "元素法师", "圣骑士", "治疗师"], + attackHeroes: ["暗影刺客", "元素法师", "圣骑士", "治疗师"], + equipmentInfo: "攻击套 + 暴击套", + artifacts: "攻击力 + 暴击率", + battleStrategy: "以高爆发伤害为主的阵容,适合快速结束战斗", + prerequisites: "需要高星级角色和优质装备", + importantNotes: "注意控制技能释放时机", + createTime: "2023-01-01", + updateTime: "2023-01-01" + }, + { + id: 2, + defenseHeroes: ["龙骑士", "冰霜法师", "守护者", "圣光牧师"], + attackHeroes: ["龙骑士", "冰霜法师", "守护者", "圣光牧师"], + equipmentInfo: "生命套 + 防御套", + artifacts: "生命值 + 防御力", + battleStrategy: "以持续输出和防御为主的阵容,适合持久战", + prerequisites: "需要高生命值和防御力的角色", + importantNotes: "注意保持治疗技能的持续释放", + createTime: "2023-01-02", + updateTime: "2023-01-02" + } + ]; + + const difficulties = ["简单", "中等", "困难"]; + const availableTags = ["PVP", "PVE", "爆发", "控制", "持续", "防守", "快速"]; + + const filteredLineups = lineups.filter(lineup => { + const matchesSearch = + lineup.battleStrategy.toLowerCase().includes(searchTerm.toLowerCase()) || + lineup.defenseHeroes.some((hero: string) => hero.toLowerCase().includes(searchTerm.toLowerCase())) || + lineup.attackHeroes.some((hero: string) => hero.toLowerCase().includes(searchTerm.toLowerCase())); + + // Since we don't have difficulty in the API data, we'll use a simple filter + const matchesDifficulty = selectedDifficulty === "All" || + (selectedDifficulty === "简单" && lineup.id % 3 === 1) || + (selectedDifficulty === "中等" && lineup.id % 3 === 2) || + (selectedDifficulty === "困难" && lineup.id % 3 === 0); + + // Since we don't have tags in the API data, we'll use a simple filter + const matchesTags = selectedTags.length === 0 || + selectedTags.some(tag => lineup.battleStrategy.toLowerCase().includes(tag.toLowerCase())); + + return matchesSearch && matchesDifficulty && matchesTags; + }); + + const handleLineupClick = (id: number) => { + navigate(`/lineup/${id}`); + }; + + const handlePageChange = (newPage: number) => { + setPageNum(newPage); + }; + + const handlePageSizeChange = (newSize: number) => { + setPageSize(newSize); + setPageNum(1); // Reset to first page when changing page size + }; + + return ( +
+ {/* 背景装饰 */} +
+ +
+

+ + 对战阵容推荐 + +

+ + {/* 搜索和筛选组件 */} +
+
+ setSearchTerm(e.target.value)} + /> +
+ + {/* 难度筛选 */} +
+
+ + {difficulties.map((difficulty) => ( + + ))} +
+
+ + {/* 标签筛选 */} +
+
+ {availableTags.map((tag) => ( + + ))} +
+
+
+ + {/* 加载状态 */} + {loading && ( +
+
+
+ )} + + {/* 错误提示 */} + {error && ( +
+ {error} +
+ )} + + {/* 阵容列表 - 列表格式 */} + {!loading && ( +
+ {filteredLineups.map((lineup) => ( +
handleLineupClick(lineup.id)} + > +
+
+
+

阵容 #{lineup.id}

+

{lineup.battleStrategy}

+
+
+ 创建时间: + {lineup.createTime} +
+
+ +
+ {/* 防守阵容 */} +
+

防守阵容

+
+ {lineup.defenseHeroes.map((hero: string, index: number) => ( + + {hero} + + ))} +
+
+ + {/* 进攻阵容 */} +
+

进攻阵容

+
+ {lineup.attackHeroes.map((hero: string, index: number) => ( + + {hero} + + ))} +
+
+
+ +
+ {/* 装备信息 */} +
+

装备信息

+

{lineup.equipmentInfo}

+
+ + {/* 神器信息 */} +
+

神器信息

+

{lineup.artifacts}

+
+
+ +
+ {/* 前置条件 */} +
+

前置条件

+

{lineup.prerequisites}

+
+ + {/* 重要提示 */} +
+

重要提示

+

{lineup.importantNotes}

+
+
+
+
+ ))} +
+ )} + + {/* 分页控件 */} + {!loading && total > 0 && ( +
+
+ + + {Array.from({ length: Math.ceil(total / pageSize) }, (_, i) => i + 1) + .filter(page => + page === 1 || + page === Math.ceil(total / pageSize) || + (page >= pageNum - 1 && page <= pageNum + 1) + ) + .map((page, index, array) => { + // Add ellipsis if there's a gap + if (index > 0 && page - array[index - 1] > 1) { + return ( + + ... + + + ); + } + + return ( + + ); + })} + + +
+ +
+ 每页显示: + +
+
+ )} +
+
+ ); +}; + +export default Lineup; \ No newline at end of file diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000..0180744 --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,42 @@ +import axios from 'axios' + +// 获取环境变量 +const getBaseUrl = () => { + if (process.env.NODE_ENV === 'development') { + return 'http://localhost:8080' + } + return 'https://api.your-domain.com' +} + +// 创建 axios 实例 +const instance = axios.create({ + baseURL: getBaseUrl(), + timeout: 10000, + headers: { + 'Content-Type': 'application/json' + } +}) + +// 请求拦截器 +instance.interceptors.request.use( + (config) => { + // 这里可以添加 token 等认证信息 + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// 响应拦截器 +instance.interceptors.response.use( + (response) => { + return response.data + }, + (error) => { + // 这里可以统一处理错误 + return Promise.reject(error) + } +) + +export const request = instance \ No newline at end of file