import { useState, useEffect } from 'react'; import { Card, Button, Modal, Form, Select, InputNumber, Input, message, Progress, Tag, Space, Divider, Typography } from 'antd'; import { EditOutlined, PlusOutlined, DeleteOutlined, TrophyOutlined } from '@ant-design/icons'; import axios from 'axios'; const { TextArea } = Input; const { Text, Paragraph } = Typography; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000'; interface CareerDetail { id: string; character_id: string; career_id: string; career_name: string; career_type: 'main' | 'sub'; current_stage: number; stage_name: string; stage_description?: string; stage_progress: number; max_stage: number; started_at?: string; reached_current_stage_at?: string; notes?: string; } interface Career { id: string; name: string; type: 'main' | 'sub'; max_stage: number; } interface Props { characterId: string; projectId: string; editable?: boolean; onUpdate?: () => void; } export const CharacterCareerCard: React.FC = ({ characterId, projectId, editable = false, onUpdate }) => { const [mainCareer, setMainCareer] = useState(null); const [subCareers, setSubCareers] = useState([]); const [allCareers, setAllCareers] = useState([]); const [loading, setLoading] = useState(true); const [isMainModalOpen, setIsMainModalOpen] = useState(false); const [isSubModalOpen, setIsSubModalOpen] = useState(false); const [isProgressModalOpen, setIsProgressModalOpen] = useState(false); const [selectedCareer, setSelectedCareer] = useState(null); const [mainForm] = Form.useForm(); const [subForm] = Form.useForm(); const [progressForm] = Form.useForm(); useEffect(() => { fetchCharacterCareers(); if (editable) { fetchAllCareers(); } }, [characterId]); const fetchCharacterCareers = async () => { try { setLoading(true); const response = await axios.get( `${API_BASE_URL}/api/careers/character/${characterId}/careers`, { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } } ); setMainCareer(response.data.main_career || null); setSubCareers(response.data.sub_careers || []); } catch (error: any) { message.error(error.response?.data?.detail || '获取职业信息失败'); } finally { setLoading(false); } }; const fetchAllCareers = async () => { try { const response = await axios.get(`${API_BASE_URL}/api/careers`, { params: { project_id: projectId }, headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } }); const main = response.data.main_careers || []; const sub = response.data.sub_careers || []; setAllCareers([...main, ...sub]); } catch (error: any) { console.error('获取职业列表失败:', error); } }; const handleSetMainCareer = async (values: any) => { try { await axios.post( `${API_BASE_URL}/api/careers/character/${characterId}/careers/main`, values, { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } } ); message.success('主职业设置成功'); setIsMainModalOpen(false); mainForm.resetFields(); fetchCharacterCareers(); onUpdate?.(); } catch (error: any) { message.error(error.response?.data?.detail || '设置主职业失败'); } }; const handleAddSubCareer = async (values: any) => { try { await axios.post( `${API_BASE_URL}/api/careers/character/${characterId}/careers/sub`, values, { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } } ); message.success('副职业添加成功'); setIsSubModalOpen(false); subForm.resetFields(); fetchCharacterCareers(); onUpdate?.(); } catch (error: any) { message.error(error.response?.data?.detail || '添加副职业失败'); } }; const handleUpdateProgress = async (values: any) => { if (!selectedCareer) return; try { await axios.put( `${API_BASE_URL}/api/careers/character/${characterId}/careers/${selectedCareer.career_id}/stage`, values, { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } } ); message.success('职业阶段更新成功'); setIsProgressModalOpen(false); progressForm.resetFields(); fetchCharacterCareers(); onUpdate?.(); } catch (error: any) { message.error(error.response?.data?.detail || '更新职业阶段失败'); } }; const handleRemoveSubCareer = (careerId: string) => { Modal.confirm({ title: '确认删除', content: '确定要移除这个副职业吗?', onOk: async () => { try { await axios.delete( `${API_BASE_URL}/api/careers/character/${characterId}/careers/${careerId}`, { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } } ); message.success('副职业删除成功'); fetchCharacterCareers(); onUpdate?.(); } catch (error: any) { message.error(error.response?.data?.detail || '删除副职业失败'); } } }); }; const openEditProgress = (career: CareerDetail) => { setSelectedCareer(career); progressForm.setFieldsValue({ current_stage: career.current_stage, stage_progress: career.stage_progress, reached_current_stage_at: career.reached_current_stage_at || '', notes: career.notes || '' }); setIsProgressModalOpen(true); }; const renderCareerInfo = (career: CareerDetail, isMain: boolean = false) => (
{career.career_name} {isMain && } {editable && (
); if (loading) { return ; } return ( <> 职业信息 } extra={ editable && !mainCareer && ( ) } > {mainCareer ? ( <> {renderCareerInfo(mainCareer, true)} {subCareers.length > 0 && ( <> 副职业
{subCareers.map(career => renderCareerInfo(career, false))}
)} {editable && subCareers.length < 5 && (
)} ) : ( 暂无职业信息 )}
{/* 设置主职业 */} setIsMainModalOpen(false)} footer={null} >
{/* 添加副职业 */} setIsSubModalOpen(false)} footer={null} >
{/* 更新职业进度 */} setIsProgressModalOpen(false)} footer={null} > {selectedCareer && (
职业:{selectedCareer.career_name}