Skip to content

Commit 93b2a7a

Browse files
committed
添加挑战列表页面的响应式布局,优化移动端显示效果
1 parent 716bebf commit 93b2a7a

File tree

6 files changed

+375
-180
lines changed

6 files changed

+375
-180
lines changed

src/components/ChallengeListPage/ChallengeControls.tsx

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { SortAscendingOutlined, SortDescendingOutlined, TagOutlined, FilterOutli
33
import { useTranslation } from 'react-i18next';
44
import { useState, ChangeEvent } from 'react';
55
import StarRating from '../StarRating';
6+
import { useMediaQuery } from 'react-responsive';
67

78
// 定义平台枚举值
89
const PLATFORM_TYPES = ['Web', 'Android', 'iOS'];
@@ -88,6 +89,7 @@ const ChallengeControls: React.FC<ChallengeControlsProps> = ({
8889
onSortOrderChange
8990
}) => {
9091
const { t } = useTranslation();
92+
const isMobile = useMediaQuery({ maxWidth: 768 });
9193

9294
// 在组件的开头部分添加状态
9395
const [tagSearchText, setTagSearchText] = useState('');
@@ -148,9 +150,9 @@ const ChallengeControls: React.FC<ChallengeControlsProps> = ({
148150
const tagMenu = (
149151
<div style={{
150152
padding: '12px',
151-
maxHeight: '400px',
153+
maxHeight: isMobile ? '300px' : '400px',
152154
overflowY: 'auto',
153-
minWidth: '300px',
155+
minWidth: isMobile ? '250px' : '300px',
154156
backgroundColor: '#fff',
155157
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
156158
borderRadius: '4px',
@@ -190,7 +192,7 @@ const ChallengeControls: React.FC<ChallengeControlsProps> = ({
190192
width: 'auto',
191193
display: 'inline-flex',
192194
alignItems: 'center',
193-
fontSize: '13px'
195+
fontSize: isMobile ? '12px' : '13px'
194196
}}
195197
>
196198
{tag}
@@ -203,39 +205,66 @@ const ChallengeControls: React.FC<ChallengeControlsProps> = ({
203205
);
204206

205207
return (
206-
<Space split={<Divider type="vertical" />} style={{ marginBottom: 20 }}>
208+
<Space
209+
split={isMobile ? null : <Divider type="vertical" />}
210+
style={{ marginBottom: isMobile ? 12 : 20 }}
211+
direction={isMobile ? "vertical" : "horizontal"}
212+
size={isMobile ? 8 : "middle"}
213+
wrap={!isMobile}
214+
>
207215
{/* 标签过滤 */}
208216
<Dropdown overlay={tagMenu} trigger={['click']}>
209-
<Button icon={<TagOutlined />}>
217+
<Button
218+
icon={<TagOutlined />}
219+
size={isMobile ? "middle" : "default"}
220+
style={{ width: isMobile ? '100%' : 'auto', justifyContent: 'space-between', display: 'flex', alignItems: 'center' }}
221+
>
210222
{t('challenges.filters.tags')} {selectedTags.length > 0 && `(${selectedTags.length})`} <DownOutlined />
211223
</Button>
212224
</Dropdown>
213225

214226
{/* 难度过滤 */}
215227
<Dropdown overlay={difficultyMenu} trigger={['click']}>
216-
<Button icon={<FilterOutlined />}>
228+
<Button
229+
icon={<FilterOutlined />}
230+
size={isMobile ? "middle" : "default"}
231+
style={{ width: isMobile ? '100%' : 'auto', justifyContent: 'space-between', display: 'flex', alignItems: 'center' }}
232+
>
217233
{t('challenges.filters.difficulty')} <DownOutlined />
218234
</Button>
219235
</Dropdown>
220236

221237
{/* 平台过滤 */}
222238
<Dropdown overlay={platformMenu} trigger={['click']}>
223-
<Button icon={<FilterOutlined />}>
239+
<Button
240+
icon={<FilterOutlined />}
241+
size={isMobile ? "middle" : "default"}
242+
style={{ width: isMobile ? '100%' : 'auto', justifyContent: 'space-between', display: 'flex', alignItems: 'center' }}
243+
>
224244
{t('challenges.controls.platform')} <DownOutlined />
225245
</Button>
226246
</Dropdown>
227247

228248
{/* 排序控制 - 移到最后 */}
229-
<Space>
249+
<Space style={{ width: isMobile ? '100%' : 'auto' }}>
230250
<Dropdown overlay={sortMenu} trigger={['click']}>
231-
<Button>
232-
{t('challenges.controls.sortBy')}: {t(`challenges.sort.${sortBy}`)} <DownOutlined />
251+
<Button
252+
size={isMobile ? "middle" : "default"}
253+
style={{
254+
width: isMobile ? 'calc(100% - 32px)' : 'auto',
255+
justifyContent: 'space-between',
256+
display: 'flex',
257+
alignItems: 'center'
258+
}}
259+
>
260+
{isMobile ? t(`challenges.sort.${sortBy}`) : `${t('challenges.controls.sortBy')}: ${t(`challenges.sort.${sortBy}`)}`} <DownOutlined />
233261
</Button>
234262
</Dropdown>
235263
<Button
236264
icon={sortOrder === 'asc' ? <SortAscendingOutlined /> : <SortDescendingOutlined />}
237265
onClick={onSortOrderChange}
238266
title={sortOrder === 'asc' ? t('challenges.controls.ascending') : t('challenges.controls.descending')}
267+
size={isMobile ? "middle" : "default"}
239268
/>
240269
</Space>
241270
</Space>

src/components/ChallengeListPage/ChallengeFilters.tsx

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { Space, Tag, Button } from 'antd';
1+
import { Space, Tag, Button, Row, Col } from 'antd';
22
import { CloseOutlined } from '@ant-design/icons';
33
import { useTranslation } from 'react-i18next';
44
import SearchBox from '../SearchBox';
55
import StarRating from '../StarRating';
6+
import { useMediaQuery } from 'react-responsive';
67

78
interface ChallengeFiltersProps {
89
/**
@@ -72,60 +73,90 @@ const ChallengeFilters: React.FC<ChallengeFiltersProps> = ({
7273
searchValue = ''
7374
}) => {
7475
const { t } = useTranslation();
76+
const isMobile = useMediaQuery({ maxWidth: 768 });
7577

7678
return (
7779
<div>
78-
{/* 搜索框组件 */}
79-
<SearchBox
80-
onSearch={onSearch}
81-
value={searchValue}
82-
placeholder={t('challenges.filters.search')}
83-
historyStorageKey="challenge-search-history"
84-
/>
80+
{/* 搜索框组件 - 移除style属性,使用div包装处理边距 */}
81+
<div style={{ marginBottom: isMobile ? '8px' : '16px' }}>
82+
<SearchBox
83+
onSearch={onSearch}
84+
value={searchValue}
85+
placeholder={t('challenges.filters.search')}
86+
historyStorageKey="challenge-search-history"
87+
/>
88+
</div>
8589

8690
{/* 已应用的过滤器 */}
8791
{hasFilters && (
88-
<Space wrap style={{ marginBottom: 16 }}>
89-
{selectedDifficulty !== 'all' && (
90-
<Tag
91-
closable
92-
onClose={onRemoveDifficulty}
93-
style={{ background: '#f0f5ff', borderColor: '#adc6ff' }}
94-
>
95-
{t('challenges.sort.difficulty')}: <StarRating difficulty={parseInt(selectedDifficulty)} />
96-
</Tag>
97-
)}
98-
99-
{selectedPlatform !== 'all' && (
100-
<Tag
101-
closable
102-
onClose={onRemovePlatform}
103-
color={selectedPlatform === 'LeetCode' ? 'orange' : 'purple'}
104-
>
105-
{t('challenge.detail.targetWebsite')}: {selectedPlatform}
106-
</Tag>
107-
)}
108-
109-
{selectedTags.map(tag => (
110-
<Tag
111-
key={tag}
112-
closable
113-
onClose={() => onRemoveTag(tag)}
114-
style={{ background: '#f6ffed', borderColor: '#b7eb8f' }}
115-
>
116-
{tag}
117-
</Tag>
118-
))}
119-
120-
<Button
121-
type="text"
122-
icon={<CloseOutlined />}
123-
onClick={onClearAll}
124-
style={{ color: '#ff4d4f' }}
125-
>
126-
{t('challenges.filters.clearAll')}
127-
</Button>
128-
</Space>
92+
<Row gutter={[8, 8]} style={{ marginBottom: isMobile ? '8px' : '16px' }}>
93+
<Col span={24}>
94+
<Space wrap size={isMobile ? 4 : 8} style={{ width: '100%' }}>
95+
{selectedDifficulty !== 'all' && (
96+
<Tag
97+
closable
98+
onClose={onRemoveDifficulty}
99+
style={{
100+
background: '#f0f5ff',
101+
borderColor: '#adc6ff',
102+
fontSize: isMobile ? '12px' : '14px',
103+
padding: isMobile ? '0 4px' : '0 7px',
104+
margin: isMobile ? '0 4px 4px 0' : '0 8px 8px 0'
105+
}}
106+
>
107+
{isMobile ? '' : `${t('challenges.sort.difficulty')}: `}
108+
<StarRating difficulty={parseInt(selectedDifficulty)} />
109+
</Tag>
110+
)}
111+
112+
{selectedPlatform !== 'all' && (
113+
<Tag
114+
closable
115+
onClose={onRemovePlatform}
116+
color={selectedPlatform === 'LeetCode' ? 'orange' : 'purple'}
117+
style={{
118+
fontSize: isMobile ? '12px' : '14px',
119+
padding: isMobile ? '0 4px' : '0 7px',
120+
margin: isMobile ? '0 4px 4px 0' : '0 8px 8px 0'
121+
}}
122+
>
123+
{isMobile ? selectedPlatform : `${t('challenge.detail.targetWebsite')}: ${selectedPlatform}`}
124+
</Tag>
125+
)}
126+
127+
{selectedTags.map(tag => (
128+
<Tag
129+
key={tag}
130+
closable
131+
onClose={() => onRemoveTag(tag)}
132+
style={{
133+
background: '#f6ffed',
134+
borderColor: '#b7eb8f',
135+
fontSize: isMobile ? '12px' : '14px',
136+
padding: isMobile ? '0 4px' : '0 7px',
137+
margin: isMobile ? '0 4px 4px 0' : '0 8px 8px 0'
138+
}}
139+
>
140+
{tag}
141+
</Tag>
142+
))}
143+
144+
<Button
145+
type="text"
146+
icon={<CloseOutlined />}
147+
onClick={onClearAll}
148+
style={{
149+
color: '#ff4d4f',
150+
fontSize: isMobile ? '12px' : '14px',
151+
height: isMobile ? '22px' : '28px',
152+
padding: isMobile ? '0 4px' : '0 8px'
153+
}}
154+
>
155+
{isMobile ? t('challenges.filters.clearAllShort') : t('challenges.filters.clearAll')}
156+
</Button>
157+
</Space>
158+
</Col>
159+
</Row>
129160
)}
130161
</div>
131162
);

0 commit comments

Comments
 (0)