import React, { useState, useEffect } from 'react';
|
import { Table, Button, Modal, Form, Input, Select, DatePicker, Space, Card, message, Popconfirm, Tag, Row, Col, Upload } from 'antd';
|
import { PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined, UserOutlined, UploadOutlined } from '@ant-design/icons';
|
import { useNavigate } from 'react-router-dom';
|
import { activityAPI } from '../../services/api';
|
import dayjs from 'dayjs';
|
|
const { Option } = Select;
|
const { RangePicker } = DatePicker;
|
|
const ActivityList = () => {
|
const [form] = Form.useForm();
|
const [modalVisible, setModalVisible] = useState(false);
|
const [editingRecord, setEditingRecord] = useState(null);
|
const [loading, setLoading] = useState(false);
|
const [searchForm] = Form.useForm();
|
const [viewModalVisible, setViewModalVisible] = useState(false);
|
const [viewingRecord, setViewingRecord] = useState(null);
|
const [activities, setActivities] = useState([]);
|
const [pagination, setPagination] = useState({
|
current: 1,
|
pageSize: 10,
|
total: 0,
|
});
|
const [filters, setFilters] = useState({});
|
|
const navigate = useNavigate();
|
|
// 获取活动列表
|
const fetchActivities = async (params = {}) => {
|
try {
|
setLoading(true);
|
const queryParams = {
|
page: pagination.current,
|
size: pagination.pageSize,
|
...filters,
|
...params,
|
};
|
|
const response = await activityAPI.getActivityList(queryParams);
|
if (response.code === 0) {
|
setActivities(response.data.content || []);
|
setPagination(prev => ({
|
...prev,
|
total: response.data.totalElements || 0,
|
}));
|
}
|
} catch (error) {
|
console.error('获取活动列表失败:', error);
|
message.error('获取活动列表失败');
|
} finally {
|
setLoading(false);
|
}
|
};
|
|
// 页面初始化加载数据
|
useEffect(() => {
|
fetchActivities();
|
}, [pagination.current, pagination.pageSize]);
|
|
// 处理分页变化
|
const handleTableChange = (paginationInfo) => {
|
setPagination(prev => ({
|
...prev,
|
current: paginationInfo.current,
|
pageSize: paginationInfo.pageSize,
|
}));
|
};
|
|
|
const columns = [
|
{
|
title: '活动名称',
|
dataIndex: 'title',
|
key: 'title',
|
width: 200,
|
},
|
{
|
title: '活动分类',
|
dataIndex: 'categoryDesc',
|
key: 'categoryDesc',
|
width: 120,
|
render: (categoryDesc) => {
|
const colorMap = {
|
'党的建设': 'red',
|
'经济发展': 'blue',
|
'平安法治': 'green',
|
'民生服务': 'orange',
|
'失信违法': 'purple',
|
};
|
return <Tag color={colorMap[categoryDesc]}>{categoryDesc}</Tag>;
|
},
|
},
|
{
|
title: '活动时间',
|
key: 'time',
|
width: 200,
|
render: (_, record) => (
|
<div>
|
<div>开始:{dayjs(record.startTime).format('MM-DD HH:mm')}</div>
|
<div>结束:{dayjs(record.endTime).format('MM-DD HH:mm')}</div>
|
</div>
|
),
|
},
|
{
|
title: '活动地点',
|
dataIndex: 'location',
|
key: 'location',
|
width: 150,
|
},
|
{
|
title: '最大志愿者人数',
|
dataIndex: 'maxParticipants',
|
key: 'maxParticipants',
|
width: 120,
|
render: (maxParticipants) => (
|
<span>
|
{maxParticipants}
|
</span>
|
),
|
},
|
{
|
title: '报名截止时间',
|
dataIndex: 'deadline',
|
key: 'deadline',
|
width: 150,
|
render: (deadline) => dayjs(deadline).format('YYYY-MM-DD HH:mm'),
|
},
|
{
|
title: '活动积分',
|
dataIndex: 'points',
|
key: 'points',
|
width: 80,
|
render: (points) => <span style={{ color: '#52c41a', fontWeight: 'bold' }}>+{points}</span>,
|
},
|
{
|
title: '状态',
|
dataIndex: 'status',
|
key: 'status',
|
width: 100,
|
render: (status) => {
|
const statusMap = {
|
1: { text: '未开始', color: 'default' },
|
2: { text: '进行中', color: 'processing' },
|
3: { text: '已结束', color: 'success' }
|
};
|
const statusInfo = statusMap[status] || { text: '未知', color: 'default' };
|
return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
|
},
|
},
|
{
|
title: '创建时间',
|
dataIndex: 'createTime',
|
key: 'createTime',
|
width: 150,
|
render: (time) => dayjs(time).format('YYYY-MM-DD HH:mm'),
|
},
|
{
|
title: '操作',
|
key: 'action',
|
width: 200,
|
render: (_, record) => (
|
<Space size="middle">
|
<Button
|
type="link"
|
icon={<EyeOutlined />}
|
onClick={() => handleView(record)}
|
>
|
查看
|
</Button>
|
<Button
|
type="link"
|
icon={<UserOutlined />}
|
onClick={() => navigate(`/activities/registrations/${record.id}`)}
|
>
|
报名管理
|
</Button>
|
{record.status !== '已结束' && (
|
<>
|
<Button
|
type="link"
|
icon={<EditOutlined />}
|
onClick={() => handleEdit(record)}
|
>
|
编辑
|
</Button>
|
<Popconfirm
|
title="确定要删除这个活动吗?"
|
onConfirm={() => handleDelete(record.id)}
|
okText="确定"
|
cancelText="取消"
|
>
|
<Button type="link" danger icon={<DeleteOutlined />}>
|
删除
|
</Button>
|
</Popconfirm>
|
</>
|
)}
|
</Space>
|
),
|
},
|
];
|
|
const handleAdd = () => {
|
navigate('/activities/create');
|
};
|
|
const handleView = (record) => {
|
setViewingRecord(record);
|
setViewModalVisible(true);
|
};
|
|
const handleEdit = (record) => {
|
navigate(`/activities/edit/${record.id}`);
|
};
|
|
const handleDelete = async (id) => {
|
try {
|
const response = await activityAPI.deleteActivities(id.toString());
|
if (response.code === 0) {
|
message.success('删除成功');
|
fetchActivities();
|
}
|
} catch (error) {
|
console.error('删除活动失败:', error);
|
message.error('删除活动失败');
|
}
|
};
|
|
const handleModalCancel = () => {
|
setViewModalVisible(false);
|
};
|
|
const handleSearch = (values) => {
|
const searchParams = { ...values };
|
if (values.timeRange && values.timeRange.length === 2) {
|
searchParams.createStart = values.timeRange[0].format('YYYY-MM-DD');
|
searchParams.createEnd = values.timeRange[1].format('YYYY-MM-DD');
|
delete searchParams.timeRange;
|
}
|
|
setFilters(searchParams);
|
setPagination(prev => ({ ...prev, current: 1 }));
|
fetchActivities({ ...searchParams, page: 1 });
|
};
|
|
const handleReset = () => {
|
searchForm.resetFields();
|
setFilters({});
|
setPagination(prev => ({ ...prev, current: 1 }));
|
fetchActivities({ page: 1 });
|
};
|
|
return (
|
<div>
|
<div className="page-header">
|
<h1 className="page-title">活动列表</h1>
|
<p className="page-description">管理所有志愿者活动,查看活动状态和报名情况</p>
|
</div>
|
|
{/* 搜索表单 */}
|
<Card className="search-form">
|
<Form
|
form={searchForm}
|
layout="inline"
|
onFinish={handleSearch}
|
>
|
<Row gutter={[16, 16]} style={{ width: '100%' }}>
|
<Col xs={24} sm={12} md={6}>
|
<Form.Item name="title" label="活动名称">
|
<Input placeholder="请输入活动名称" />
|
</Form.Item>
|
</Col>
|
<Col xs={24} sm={12} md={6}>
|
<Form.Item name="category" label="活动分类">
|
<Select placeholder="请选择活动分类" allowClear>
|
<Option value="党的建设">党的建设</Option>
|
<Option value="经济发展">经济发展</Option>
|
<Option value="平安法治">平安法治</Option>
|
<Option value="民生服务">民生服务</Option>
|
<Option value="失信违法">失信违法</Option>
|
</Select>
|
</Form.Item>
|
</Col>
|
<Col xs={24} sm={12} md={6}>
|
<Form.Item name="status" label="活动状态">
|
<Select placeholder="请选择活动状态" allowClear>
|
<Option value="报名中">报名中</Option>
|
<Option value="进行中">进行中</Option>
|
<Option value="已结束">已结束</Option>
|
<Option value="已取消">已取消</Option>
|
</Select>
|
</Form.Item>
|
</Col>
|
<Col xs={24} sm={12} md={6}>
|
<Form.Item name="timeRange" label="活动时间">
|
<RangePicker style={{ width: '100%' }} />
|
</Form.Item>
|
</Col>
|
</Row>
|
<div className="form-actions">
|
<Button type="primary" htmlType="submit">
|
搜索
|
</Button>
|
<Button onClick={handleReset}>
|
重置
|
</Button>
|
</div>
|
</Form>
|
</Card>
|
|
<Card>
|
<div className="table-actions">
|
<Button
|
type="primary"
|
icon={<PlusOutlined />}
|
onClick={handleAdd}
|
>
|
发布活动
|
</Button>
|
</div>
|
|
<Table
|
columns={columns}
|
dataSource={activities}
|
rowKey="id"
|
loading={loading}
|
scroll={{ x: 1400 }}
|
pagination={{
|
current: pagination.current,
|
pageSize: pagination.pageSize,
|
total: pagination.total,
|
showSizeChanger: true,
|
showQuickJumper: true,
|
showTotal: (total, range) => `第 ${range[0]}-${range[1]} 条/共 ${total} 条`,
|
}}
|
onChange={handleTableChange}
|
/>
|
</Card>
|
|
{/* 查看活动弹窗 */}
|
<Modal
|
title="查看活动详情"
|
open={viewModalVisible}
|
onCancel={handleModalCancel}
|
footer={[
|
<Button key="close" onClick={handleModalCancel}>
|
关闭
|
</Button>
|
]}
|
width={800}
|
>
|
{viewingRecord && (
|
<div>
|
<Row gutter={[24, 16]}>
|
<Col span={12}>
|
<div><strong>活动名称:</strong>{viewingRecord.title}</div>
|
</Col>
|
<Col span={12}>
|
<div><strong>活动分类:</strong>{viewingRecord.categoryDesc}</div>
|
</Col>
|
</Row>
|
<Row gutter={[24, 16]}>
|
<Col span={12}>
|
<div><strong>活动开始时间:</strong>{dayjs(viewingRecord.startTime).format('YYYY-MM-DD HH:mm')}</div>
|
</Col>
|
<Col span={12}>
|
<div><strong>活动结束时间:</strong>{dayjs(viewingRecord.endTime).format('YYYY-MM-DD HH:mm')}</div>
|
</Col>
|
</Row>
|
<Row gutter={[24, 16]}>
|
<Col span={12}>
|
<div><strong>活动地点:</strong>{viewingRecord.location}</div>
|
</Col>
|
<Col span={12}>
|
<div><strong>报名截止时间:</strong>{dayjs(viewingRecord.deadline).format('YYYY-MM-DD HH:mm')}</div>
|
</Col>
|
</Row>
|
<Row gutter={[24, 16]}>
|
<Col span={8}>
|
<div><strong>最大志愿者人数:</strong>{viewingRecord.maxParticipants}</div>
|
</Col>
|
<Col span={8}>
|
<div><strong>活动积分:</strong>{viewingRecord.points}</div>
|
</Col>
|
<Col span={8}>
|
<div><strong>状态:</strong>{
|
(() => {
|
const statusMap = {
|
1: '未开始',
|
2: '进行中',
|
3: '已结束'
|
};
|
return statusMap[viewingRecord.status] || '未知';
|
})()
|
}</div>
|
</Col>
|
</Row>
|
<Row gutter={[24, 16]}>
|
<Col span={12}>
|
<div><strong>创建时间:</strong>{dayjs(viewingRecord.createTime).format('YYYY-MM-DD HH:mm')}</div>
|
</Col>
|
</Row>
|
<Row gutter={[24, 16]}>
|
<Col span={24}>
|
<div><strong>活动内容:</strong></div>
|
<div style={{ marginTop: '8px', padding: '12px', backgroundColor: '#f5f5f5', borderRadius: '4px' }}>
|
{viewingRecord.content}
|
</div>
|
</Col>
|
</Row>
|
{viewingRecord.cover && (
|
<Row gutter={[24, 16]}>
|
<Col span={24}>
|
<div><strong>活动封面:</strong></div>
|
<div style={{ marginTop: '8px' }}>
|
<img src={viewingRecord.cover} alt="活动封面" style={{ maxWidth: '200px', borderRadius: '4px' }} />
|
</div>
|
</Col>
|
</Row>
|
)}
|
</div>
|
)}
|
</Modal>
|
</div>
|
);
|
};
|
|
export default ActivityList;
|