This commit is contained in:
@@ -12,6 +12,16 @@ import dayjs from 'dayjs';
|
||||
import { getCompetitionDetail, getProjects, getMyCompetitionEnrollment, enrollCompetition } from '../../api';
|
||||
import ProjectSubmission from './ProjectSubmission';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import 'github-markdown-css/github-markdown-dark.css';
|
||||
|
||||
const getImageUrl = (url) => {
|
||||
if (!url) return '';
|
||||
if (url.startsWith('http') || url.startsWith('//')) return url;
|
||||
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8000/api';
|
||||
// Remove /api suffix if present to get the root URL for media files
|
||||
const baseUrl = apiUrl.replace(/\/api\/?$/, '');
|
||||
return `${baseUrl}${url}`;
|
||||
};
|
||||
|
||||
const { Title, Paragraph } = Typography;
|
||||
|
||||
@@ -77,19 +87,19 @@ const CompetitionDetail = () => {
|
||||
// Fetch competition details
|
||||
const { data: competition, isLoading: loadingDetail } = useQuery({
|
||||
queryKey: ['competition', id],
|
||||
queryFn: () => getCompetitionDetail(id)
|
||||
queryFn: () => getCompetitionDetail(id).then(res => res.data)
|
||||
});
|
||||
|
||||
// Fetch projects (for leaderboard/display)
|
||||
const { data: projects } = useQuery({
|
||||
queryKey: ['projects', id],
|
||||
queryFn: () => getProjects({ competition: id, status: 'submitted' })
|
||||
queryFn: () => getProjects({ competition: id, status: 'submitted' }).then(res => res.data)
|
||||
});
|
||||
|
||||
// Check enrollment status
|
||||
const { data: enrollment, refetch: refetchEnrollment } = useQuery({
|
||||
queryKey: ['enrollment', id],
|
||||
queryFn: () => getMyCompetitionEnrollment(id),
|
||||
queryFn: () => getMyCompetitionEnrollment(id).then(res => res.data),
|
||||
enabled: !!user,
|
||||
retry: false
|
||||
});
|
||||
@@ -140,7 +150,7 @@ const CompetitionDetail = () => {
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
components={{
|
||||
code: CodeBlock,
|
||||
img: (props) => <img {...props} style={{ maxWidth: '100%', borderRadius: '8px' }} />,
|
||||
img: (props) => <img {...props} src={getImageUrl(props.src)} style={{ maxWidth: '100%', borderRadius: '8px' }} />,
|
||||
h1: (props) => <h1 {...props} style={{ color: '#fff', borderBottom: '1px solid #333', paddingBottom: '0.3em' }} />,
|
||||
h2: (props) => <h2 {...props} style={{ color: '#fff', borderBottom: '1px solid #333', paddingBottom: '0.3em' }} />,
|
||||
h3: (props) => <h3 {...props} style={{ color: '#eee' }} />,
|
||||
@@ -162,7 +172,7 @@ const CompetitionDetail = () => {
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
components={{
|
||||
code: CodeBlock,
|
||||
img: (props) => <img {...props} style={{ maxWidth: '100%', borderRadius: '8px' }} />,
|
||||
img: (props) => <img {...props} src={getImageUrl(props.src)} style={{ maxWidth: '100%', borderRadius: '8px' }} />,
|
||||
h1: (props) => <h1 {...props} style={{ color: '#fff', borderBottom: '1px solid #333', paddingBottom: '0.3em' }} />,
|
||||
h2: (props) => <h2 {...props} style={{ color: '#fff', borderBottom: '1px solid #333', paddingBottom: '0.3em' }} />,
|
||||
h3: (props) => <h3 {...props} style={{ color: '#eee' }} />,
|
||||
@@ -184,7 +194,7 @@ const CompetitionDetail = () => {
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
components={{
|
||||
code: CodeBlock,
|
||||
img: (props) => <img {...props} style={{ maxWidth: '100%', borderRadius: '8px' }} />,
|
||||
img: (props) => <img {...props} src={getImageUrl(props.src)} style={{ maxWidth: '100%', borderRadius: '8px' }} />,
|
||||
h1: (props) => <h1 {...props} style={{ color: '#fff', borderBottom: '1px solid #333', paddingBottom: '0.3em' }} />,
|
||||
h2: (props) => <h2 {...props} style={{ color: '#fff', borderBottom: '1px solid #333', paddingBottom: '0.3em' }} />,
|
||||
h3: (props) => <h3 {...props} style={{ color: '#eee' }} />,
|
||||
@@ -210,14 +220,18 @@ const CompetitionDetail = () => {
|
||||
<Col key={project.id} xs={24} sm={12} md={8}>
|
||||
<Card
|
||||
hoverable
|
||||
cover={<img alt={project.title} src={project.display_cover_image || 'placeholder.jpg'} style={{ height: 180, objectFit: 'cover' }} />}
|
||||
cover={<img alt={project.title} src={getImageUrl(project.display_cover_image) || 'placeholder.jpg'} style={{ height: 180, objectFit: 'cover' }} />}
|
||||
actions={[
|
||||
<Button type="link" onClick={() => navigate(`/projects/${project.id}`)}>查看详情</Button>
|
||||
]}
|
||||
>
|
||||
<Card.Meta
|
||||
title={project.title}
|
||||
description={`得分: ${project.final_score || '待定'}`}
|
||||
description={
|
||||
enrollment && project.contestant === enrollment.id
|
||||
? `得分: ${project.final_score || '待定'}`
|
||||
: null
|
||||
}
|
||||
avatar={<UserOutlined />}
|
||||
/>
|
||||
</Card>
|
||||
@@ -245,8 +259,8 @@ const CompetitionDetail = () => {
|
||||
<div style={{ color: '#888', fontSize: 12 }}>{project.contestant_info?.nickname}</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 24, color: '#00b96b', fontWeight: 'bold' }}>
|
||||
{project.final_score}
|
||||
</div>
|
||||
{enrollment && project.contestant === enrollment.id ? project.final_score : '**'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Card>
|
||||
@@ -258,7 +272,7 @@ const CompetitionDetail = () => {
|
||||
<div style={{ maxWidth: 1200, margin: '0 auto', padding: '24px' }}>
|
||||
<div style={{
|
||||
height: 300,
|
||||
backgroundImage: `url(${competition.display_cover_image})`,
|
||||
backgroundImage: `url(${getImageUrl(competition.display_cover_image)})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
borderRadius: 16,
|
||||
|
||||
Reference in New Issue
Block a user