/**
|
* @author 韩天尊
|
* @time 2024-01-15
|
* @version 1.0.0
|
* @description 活动列表页面组件
|
*/
|
import React, { useState, useEffect } from 'react';
|
import { useNavigate } from 'react-router-dom';
|
import { useAppContext } from '../context/AppContext';
|
import PageHeader from '../components/PageHeader';
|
import { formatActivityTime } from '../utils/timeFormatter';
|
import { APP_CONFIG } from '../config/appConfig';
|
|
const ActivityListPage: React.FC = () => {
|
const navigate = useNavigate();
|
const { state, loadActivities } = useAppContext();
|
const [activeTab, setActiveTab] = useState<'all' | 'joined' | 'not-joined'>('all');
|
const [communityFilter, setCommunityFilter] = useState<'all' | 'current'>('all');
|
const [searchKeyword, setSearchKeyword] = useState('');
|
const [selectedCategory, setSelectedCategory] = useState('');
|
const [showFilterModal, setShowFilterModal] = useState(false);
|
const [loading, setLoading] = useState(false);
|
|
// 重新请求活动列表数据
|
const refreshActivities = async (customParams?: any) => {
|
try {
|
setLoading(true);
|
const params: any = {
|
page: 1,
|
size: 100, // 获取更多数据用于前端筛选
|
};
|
|
// 如果传入了自定义参数,使用自定义参数
|
if (customParams) {
|
Object.assign(params, customParams);
|
} else {
|
// 添加Tab筛选参数
|
switch (activeTab) {
|
case 'all':
|
params.selectType = 'all';
|
break;
|
case 'joined':
|
params.selectType = '1';
|
break;
|
case 'not-joined':
|
params.selectType = '2';
|
break;
|
default:
|
params.selectType = 'all';
|
}
|
|
// 添加关键字搜索参数
|
if (searchKeyword.trim()) {
|
params.keyword = searchKeyword.trim();
|
}
|
|
// 添加分类筛选参数
|
if (selectedCategory) {
|
params.category = selectedCategory;
|
}
|
|
// 根据社区筛选添加参数
|
if (communityFilter === 'current' && state.user?.community) {
|
params.community = state.user.community;
|
}
|
}
|
|
await loadActivities(params);
|
} catch (error) {
|
console.error('刷新活动列表失败:', error);
|
} finally {
|
setLoading(false);
|
}
|
};
|
|
// 组件初始化时加载数据
|
useEffect(() => {
|
refreshActivities();
|
}, []);
|
|
const handleTabChange = (tab: 'all' | 'joined' | 'not-joined') => {
|
setActiveTab(tab);
|
|
// Tab切换时重新请求数据,直接使用新的tab参数
|
const tabParams: any = {
|
page: 1,
|
size: 100,
|
};
|
|
// 根据新的tab设置selectType参数
|
switch (tab) {
|
case 'all':
|
tabParams.selectType = 'all';
|
break;
|
case 'joined':
|
tabParams.selectType = '1';
|
break;
|
case 'not-joined':
|
tabParams.selectType = '2';
|
break;
|
default:
|
tabParams.selectType = 'all';
|
}
|
|
// 添加关键字搜索参数
|
if (searchKeyword.trim()) {
|
tabParams.keyword = searchKeyword.trim();
|
}
|
|
// 添加分类筛选参数
|
if (selectedCategory) {
|
tabParams.category = selectedCategory;
|
}
|
|
// 根据社区筛选添加参数
|
if (communityFilter === 'current' && state.user?.community) {
|
tabParams.community = state.user.community;
|
}
|
|
setTimeout(() => {
|
refreshActivities(tabParams);
|
}, 100);
|
};
|
|
const handleCommunityFilter = (filter: 'all' | 'current') => {
|
setCommunityFilter(filter);
|
// 社区筛选改变时重新请求数据
|
setTimeout(() => {
|
refreshActivities();
|
}, 100);
|
};
|
|
const handleActivityClick = (activityId: number) => {
|
navigate(`/activity-detail/${activityId}`);
|
};
|
|
const handleSearch = () => {
|
// 搜索时重新请求数据
|
refreshActivities();
|
};
|
|
const handleCategoryFilter = (category: string) => {
|
setSelectedCategory(category);
|
// 分类筛选改变时重新请求数据
|
setTimeout(() => {
|
refreshActivities();
|
}, 100);
|
};
|
|
// 显示筛选弹窗
|
const handleShowFilter = () => {
|
setShowFilterModal(true);
|
};
|
|
// 关闭筛选弹窗
|
const handleCloseFilter = () => {
|
setShowFilterModal(false);
|
};
|
|
// 重置筛选条件
|
const handleResetFilter = () => {
|
setSelectedCategory('');
|
setSearchKeyword('');
|
setShowFilterModal(false);
|
|
// 重置后重新请求数据,使用重置后的参数
|
const resetParams: any = {
|
page: 1,
|
size: 100,
|
};
|
|
// 添加Tab筛选参数
|
switch (activeTab) {
|
case 'all':
|
resetParams.selectType = 'all';
|
break;
|
case 'joined':
|
resetParams.selectType = '1';
|
break;
|
case 'not-joined':
|
resetParams.selectType = '2';
|
break;
|
default:
|
resetParams.selectType = 'all';
|
}
|
|
// 根据社区筛选添加参数(保留社区筛选)
|
if (communityFilter === 'current' && state.user?.community) {
|
resetParams.community = state.user.community;
|
}
|
|
refreshActivities(resetParams);
|
};
|
|
// 直接使用接口返回的数据,不再进行前端筛选
|
// 因为后端已经根据selectType参数返回了对应的数据
|
const getFilteredActivities = () => {
|
return state.activities;
|
};
|
|
const filteredActivities = getFilteredActivities();
|
|
// 获取状态文本
|
const getStatusText = (status: string | number) => {
|
const statusMap: { [key: string]: string } = {
|
'1': '报名中',
|
'2': '进行中',
|
'3': '已结束',
|
'ongoing': '进行中',
|
'upcoming': '即将开始',
|
'completed': '已结束'
|
};
|
return statusMap[status.toString()] || status.toString();
|
};
|
|
// 获取分类文本
|
const getCategoryText = (category: string) => {
|
switch (category) {
|
case 'party': return '党的建设';
|
case 'economy': return '经济发展';
|
case 'security': return '平安法治';
|
case 'service': return '民生服务';
|
case 'illegal': return '失信违法';
|
default: return category;
|
}
|
};
|
|
// 获取分类标签样式
|
const getCategoryTagClass = (category: string) => {
|
switch (category) {
|
case 'party': return 'category-tag party';
|
case 'economy': return 'category-tag economy';
|
case 'security': return 'category-tag security';
|
case 'service': return 'category-tag service';
|
case 'illegal': return 'category-tag illegal';
|
default: return 'category-tag';
|
}
|
};
|
|
// 构建完整的图片URL
|
const buildImageUrl = (filePath: string) => {
|
// 如果已经是完整URL,直接返回
|
if (filePath.startsWith('http')) {
|
return filePath;
|
}
|
// 否则拼接基础路径
|
return `${APP_CONFIG.API.SYS_BASE_URL}${filePath}`;
|
};
|
|
return (
|
<div className="page">
|
<PageHeader title="活动列表" />
|
|
{/* 搜索与筛选 */}
|
<div className="declaration-search-bar">
|
<div className="search-filter-row">
|
<div className="search-box declaration-search">
|
<i className="fas fa-search"></i>
|
<input
|
type="text"
|
placeholder="请输入活动名称"
|
id="activity-search-input"
|
value={searchKeyword}
|
onChange={(e) => setSearchKeyword(e.target.value)}
|
onKeyPress={(e) => {
|
if (e.key === 'Enter') {
|
handleSearch();
|
}
|
}}
|
/>
|
<button className="declaration-search-btn" onClick={handleSearch}>
|
搜索
|
</button>
|
</div>
|
<button className="filter-more-btn" onClick={handleShowFilter}>
|
<i className="fas fa-filter"></i>
|
更多
|
</button>
|
</div>
|
</div>
|
|
{/* 顶部Tab切换 */}
|
<div className="declaration-tabs">
|
<button
|
className={`declaration-tab ${activeTab === 'all' ? 'active' : ''}`}
|
onClick={() => handleTabChange('all')}
|
>
|
全部
|
</button>
|
<button
|
className={`declaration-tab ${activeTab === 'joined' ? 'active' : ''}`}
|
onClick={() => handleTabChange('joined')}
|
>
|
我已参加
|
</button>
|
<button
|
className={`declaration-tab ${activeTab === 'not-joined' ? 'active' : ''}`}
|
onClick={() => handleTabChange('not-joined')}
|
>
|
我未参加
|
</button>
|
</div>
|
|
{/* 社区筛选 */}
|
<div className="community-filter">
|
<button
|
className={`community-btn ${communityFilter === 'all' ? 'active' : ''}`}
|
onClick={() => handleCommunityFilter('all')}
|
>
|
全区
|
</button>
|
<button
|
className={`community-btn ${communityFilter === 'current' ? 'active' : ''}`}
|
onClick={() => handleCommunityFilter('current')}
|
>
|
本社区
|
</button>
|
</div>
|
|
{/* 活动列表 */}
|
<div className="activity-grid">
|
{loading ? (
|
<div className="loading">
|
<i className="fas fa-spinner fa-spin"></i>
|
<p>加载中...</p>
|
</div>
|
) : filteredActivities.length > 0 ? (
|
filteredActivities.map(activity => (
|
<div
|
key={activity.id}
|
className="activity-item"
|
onClick={() => handleActivityClick(activity.id)}
|
>
|
<img
|
className="cover"
|
src={
|
// 优先显示fileList中的第一张图片
|
activity.fileList && activity.fileList.length > 0
|
? buildImageUrl(activity.fileList[0])
|
: activity.img || require('../image/image1.jpg')
|
}
|
alt="活动封面"
|
/>
|
<div className="activity-info">
|
<div className="activity-info-row">
|
<h4>{activity.title}</h4>
|
<div className={`activity-status ${activity.status}`}>
|
{getStatusText(activity.status)}
|
</div>
|
</div>
|
<div className="activity-info-detail">
|
<i className="fas fa-clock"></i>服务类型:
|
<span className={`category-tag ${activity.category}`}>
|
{activity.categoryDesc || getCategoryText(activity.category)}
|
</span>
|
</div>
|
<div className="activity-info-detail">
|
<i className="fas fa-clock"></i>时间:{formatActivityTime(activity.startTime, activity.endTime, activity.time)}
|
</div>
|
<div className="activity-info-detail">
|
<i className="fas fa-map-marker-alt"></i>地点:{activity.location}
|
</div>
|
<div className="activity-info-detail">
|
<i className="fas fa-coins"></i>活动积分:<span className="activity-points">{activity.points}</span>
|
</div>
|
</div>
|
</div>
|
))
|
) : (
|
<div className="no-data">
|
<i className="fas fa-inbox"></i>
|
<p>暂无活动数据</p>
|
</div>
|
)}
|
</div>
|
|
{/* 筛选弹窗 */}
|
{showFilterModal && (
|
<div className="filter-modal-overlay" onClick={handleCloseFilter}>
|
<div className="filter-modal" onClick={(e) => e.stopPropagation()}>
|
<div className="filter-modal-header">
|
<h3>筛选条件</h3>
|
<button className="filter-close-btn" onClick={handleCloseFilter}>
|
<i className="fas fa-times"></i>
|
</button>
|
</div>
|
<div className="filter-modal-content">
|
<div className="filter-group">
|
<label>活动分类</label>
|
<select
|
className="filter-select"
|
value={selectedCategory}
|
onChange={(e) => handleCategoryFilter(e.target.value)}
|
>
|
<option value="">全部分类</option>
|
<option value="1">党的建设</option>
|
<option value="2">经济发展</option>
|
<option value="3">平安法治</option>
|
<option value="4">民生服务</option>
|
<option value="5">失信违法</option>
|
</select>
|
</div>
|
</div>
|
<div className="filter-modal-actions">
|
<button className="filter-reset-btn" onClick={handleResetFilter}>
|
重置
|
</button>
|
<button className="filter-confirm-btn" onClick={() => {
|
handleCloseFilter();
|
// 确定时重新请求数据
|
setTimeout(() => {
|
refreshActivities();
|
}, 100);
|
}}>
|
确定
|
</button>
|
</div>
|
</div>
|
</div>
|
)}
|
</div>
|
);
|
};
|
|
export default ActivityListPage;
|