Skip to content

Commit a21c79b

Browse files
committed
添加挑战详情页的响应式布局,优化移动端显示效果
1 parent 721da19 commit a21c79b

File tree

9 files changed

+367
-208
lines changed

9 files changed

+367
-208
lines changed

src/components/ChallengeDetailPage/ChallengeActions.tsx

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,42 @@
1-
import { Link } from 'react-router-dom';
1+
import { Button } from 'antd';
2+
import { HomeOutlined } from '@ant-design/icons';
3+
import { useNavigate } from 'react-router-dom';
24
import { useTranslation } from 'react-i18next';
3-
import { Challenge } from '../../types/challenge';
45

56
interface ChallengeActionsProps {
6-
challenge: Challenge;
7+
challenge: any;
8+
/**
9+
* 是否为移动端视图
10+
*/
11+
isMobile?: boolean;
712
}
813

914
/**
10-
* 挑战操作区组件,包含外部链接和返回按钮
15+
* 挑战详情页面底部操作按钮组件
1116
*/
12-
const ChallengeActions: React.FC<ChallengeActionsProps> = ({ challenge }) => {
17+
const ChallengeActions: React.FC<ChallengeActionsProps> = ({ challenge, isMobile = false }) => {
18+
const navigate = useNavigate();
1319
const { t } = useTranslation();
1420

1521
return (
16-
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
17-
<div>
18-
<a
19-
href={challenge.externalLink}
20-
target="_blank"
21-
rel="noopener noreferrer"
22-
style={{
23-
backgroundColor: '#1890ff',
24-
color: 'white',
25-
padding: '8px 16px',
26-
borderRadius: '4px',
27-
textDecoration: 'none'
28-
}}
29-
>
30-
{t('challenge.detail.startChallenge')}
31-
</a>
32-
</div>
33-
<Link to="/challenges" style={{ color: '#1890ff' }}>
34-
{t('challenge.actions.backToList')}
35-
</Link>
22+
<div style={{
23+
display: 'flex',
24+
justifyContent: 'center',
25+
width: '100%',
26+
marginTop: isMobile ? '8px' : '16px'
27+
}}>
28+
<Button
29+
type="primary"
30+
icon={<HomeOutlined />}
31+
onClick={() => navigate('/challenges')}
32+
size={isMobile ? "middle" : "large"}
33+
style={{
34+
width: isMobile ? '100%' : 'auto',
35+
maxWidth: '400px'
36+
}}
37+
>
38+
{t('challenge.detail.backToList')}
39+
</Button>
3640
</div>
3741
);
3842
};

src/components/ChallengeDetailPage/ChallengeDescription.tsx

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ const { Title } = Typography;
1010

1111
interface ChallengeDescriptionProps {
1212
challenge: Challenge;
13+
/**
14+
* 是否为移动端视图
15+
*/
16+
isMobile?: boolean;
1317
}
1418

1519
// 测试图片 - 1x1像素透明PNG
@@ -82,7 +86,7 @@ const MarkdownLink = (props: any) => {
8286
/**
8387
* 挑战描述组件,显示问题的Markdown描述
8488
*/
85-
const ChallengeDescription: React.FC<ChallengeDescriptionProps> = ({ challenge }) => {
89+
const ChallengeDescription: React.FC<ChallengeDescriptionProps> = ({ challenge, isMobile = false }) => {
8690
const { t, i18n } = useTranslation();
8791

8892
// 根据当前语言选择显示描述
@@ -217,15 +221,26 @@ const ChallengeDescription: React.FC<ChallengeDescriptionProps> = ({ challenge }
217221
if (extractedImageUrl) {
218222
return (
219223
<div>
220-
<Title level={3}>{t('challenge.detail.description')}</Title>
224+
<Title
225+
level={isMobile ? 4 : 3}
226+
style={{
227+
marginBottom: isMobile ? '12px' : '24px',
228+
fontSize: isMobile ? '18px' : '24px'
229+
}}
230+
>
231+
{t('challenge.detail.description')}
232+
</Title>
221233

222234
<Card
223235
bordered={false}
224236
style={{
225-
marginBottom: 24,
237+
marginBottom: isMobile ? 16 : 24,
226238
wordWrap: 'break-word',
227239
overflowWrap: 'break-word'
228240
}}
241+
bodyStyle={{
242+
padding: isMobile ? '12px' : '24px'
243+
}}
229244
>
230245
<div className="markdown-content">
231246
{htmlContent && (
@@ -237,7 +252,7 @@ const ChallengeDescription: React.FC<ChallengeDescriptionProps> = ({ challenge }
237252
style={{
238253
maxWidth: '100%',
239254
borderRadius: '4px',
240-
margin: '16px 0',
255+
margin: isMobile ? '8px 0' : '16px 0',
241256
display: 'block'
242257
}}
243258
preview={{
@@ -248,9 +263,7 @@ const ChallengeDescription: React.FC<ChallengeDescriptionProps> = ({ challenge }
248263
点击图片外区域关闭 | 滚轮缩放 | 左键拖动
249264
</div>
250265
),
251-
rootClassName: 'custom-image-preview'
252266
}}
253-
fallback={FALLBACK_IMAGE}
254267
/>
255268
</div>
256269
</Card>
@@ -259,54 +272,46 @@ const ChallengeDescription: React.FC<ChallengeDescriptionProps> = ({ challenge }
259272
}
260273
}
261274

262-
// 默认渲染方式 - 使用ReactMarkdown
275+
// 正常处理Markdown内容
263276
return (
264277
<div>
265-
<Title level={3}>{t('challenge.detail.description')}</Title>
278+
<Title
279+
level={isMobile ? 4 : 3}
280+
style={{
281+
marginBottom: isMobile ? '12px' : '24px',
282+
fontSize: isMobile ? '18px' : '24px'
283+
}}
284+
>
285+
{t('challenge.detail.description')}
286+
</Title>
266287

267-
{/* 实际挑战描述 */}
268-
{displayDescription ? (
269-
<Card
270-
bordered={false}
271-
style={{
272-
marginBottom: 24,
273-
wordWrap: 'break-word',
274-
overflowWrap: 'break-word'
275-
}}
276-
>
277-
<div className="markdown-content">
278-
<ReactMarkdown
288+
<Card
289+
bordered={false}
290+
style={{
291+
marginBottom: isMobile ? 16 : 24,
292+
wordWrap: 'break-word',
293+
overflowWrap: 'break-word'
294+
}}
295+
bodyStyle={{
296+
padding: isMobile ? '12px' : '24px'
297+
}}
298+
>
299+
{displayDescription ? (
300+
<div className="markdown-content" style={{ fontSize: isMobile ? '14px' : '16px' }}>
301+
<ReactMarkdown
279302
rehypePlugins={[rehypeRaw]}
280303
components={{
281-
img: MarkdownImage,
282304
a: MarkdownLink,
283-
pre: (props: any) => (
284-
<pre style={{
285-
overflowX: 'auto',
286-
whiteSpace: 'pre-wrap',
287-
wordWrap: 'break-word',
288-
maxWidth: '100%'
289-
}} {...props} />
290-
),
291-
code: (props: any) => (
292-
<code style={{
293-
overflowWrap: 'break-word',
294-
wordWrap: 'break-word',
295-
wordBreak: 'break-word'
296-
}} {...props} />
297-
)
305+
img: MarkdownImage,
298306
}}
299307
>
300308
{displayDescription}
301309
</ReactMarkdown>
302310
</div>
303-
</Card>
304-
) : (
305-
<Empty
306-
description={t('challenge.detail.noDescription', '暂无详细描述')}
307-
style={{ marginTop: 24, marginBottom: 24 }}
308-
/>
309-
)}
311+
) : (
312+
<Empty description={t('challenge.detail.noDescription')} />
313+
)}
314+
</Card>
310315
</div>
311316
);
312317
};

src/components/ChallengeDetailPage/ChallengeHeader.tsx

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@ const { Title } = Typography;
99

1010
interface ChallengeHeaderProps {
1111
challenge: Challenge;
12+
/**
13+
* 是否为移动端视图
14+
*/
15+
isMobile?: boolean;
1216
}
1317

1418
/**
1519
* 挑战详情页的标题头部组件
1620
*/
17-
const ChallengeHeader: React.FC<ChallengeHeaderProps> = ({ challenge }) => {
21+
const ChallengeHeader: React.FC<ChallengeHeaderProps> = ({ challenge, isMobile = false }) => {
1822
const navigate = useNavigate();
1923
const { t, i18n } = useTranslation();
2024

@@ -88,29 +92,47 @@ const ChallengeHeader: React.FC<ChallengeHeaderProps> = ({ challenge }) => {
8892
});
8993

9094
return (
91-
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', width: '100%' }}>
92-
<Space align="center">
95+
<div style={{
96+
display: 'flex',
97+
flexDirection: isMobile ? 'column' : 'row',
98+
justifyContent: 'space-between',
99+
alignItems: isMobile ? 'flex-start' : 'center',
100+
width: '100%',
101+
gap: isMobile ? '12px' : '0'
102+
}}>
103+
<Space align="center" wrap style={{ marginBottom: isMobile ? '8px' : 0 }}>
93104
<IdTag
94105
id={displayId}
95106
clickable
96107
onClick={handleIdClick}
97108
/>
98-
<Title level={2} style={{ margin: 0 }}>{displayTitle}</Title>
109+
<Title
110+
level={isMobile ? 3 : 2}
111+
style={{
112+
margin: 0,
113+
fontSize: isMobile ? '18px' : '24px',
114+
lineHeight: isMobile ? '24px' : '32px',
115+
wordBreak: 'break-word'
116+
}}
117+
>
118+
{displayTitle}
119+
</Title>
99120
{challenge.isExpired && (
100121
<Badge status="error" text={t('challenge.expired.linkStatus')} />
101122
)}
102123
</Space>
103124

104-
<Space>
125+
<Space size={isMobile ? 'small' : 'middle'}>
105126
{challenge.sourceFile && (
106127
<Tooltip title={t('challenge.detail.correctionTooltip')}>
107128
<Button
108129
type="link"
109130
icon={<GithubOutlined />}
110131
onClick={handleCorrectClick}
111-
size="small"
132+
size={isMobile ? "small" : "small"}
133+
style={{ padding: isMobile ? '0 4px' : '0 8px' }}
112134
>
113-
{t('challenge.detail.correction')}
135+
{isMobile ? '' : t('challenge.detail.correction')}
114136
</Button>
115137
</Tooltip>
116138
)}
@@ -120,9 +142,10 @@ const ChallengeHeader: React.FC<ChallengeHeaderProps> = ({ challenge }) => {
120142
type="link"
121143
icon={<QuestionCircleOutlined />}
122144
onClick={handleIssueClick}
123-
size="small"
145+
size={isMobile ? "small" : "small"}
146+
style={{ padding: isMobile ? '0 4px' : '0 8px' }}
124147
>
125-
{t('challenge.detail.reportIssue')}
148+
{isMobile ? '' : t('challenge.detail.reportIssue')}
126149
</Button>
127150
</Tooltip>
128151
</Space>

0 commit comments

Comments
 (0)