ym923 | bfa214f | 2025-06-09 18:10:32 +0800 | [diff] [blame] | 1 | import React, { useEffect, useState } from 'react'; |
| 2 | import { |
| 3 | Table, Button, Modal, Image, message, Tag, |
| 4 | Space, Input, Tooltip, Form, Upload |
| 5 | } from 'antd'; |
| 6 | import { |
| 7 | ExclamationCircleOutlined, SearchOutlined, UploadOutlined |
| 8 | } from '@ant-design/icons'; |
| 9 | import { |
| 10 | getAllActivities, |
| 11 | searchActivitiesByTitle, |
| 12 | deleteActivity, |
| 13 | createActivity, |
| 14 | updateActivity |
| 15 | } from '../api/activity'; |
| 16 | |
| 17 | const { confirm } = Modal; |
| 18 | |
| 19 | const ActivityAdminPanel = () => { |
| 20 | const [activities, setActivities] = useState([]); |
| 21 | const [loading, setLoading] = useState(false); |
| 22 | const [keyword, setKeyword] = useState(''); |
| 23 | const [modalVisible, setModalVisible] = useState(false); |
| 24 | const [editMode, setEditMode] = useState(false); |
| 25 | const [currentActivity, setCurrentActivity] = useState(null); |
| 26 | const [form] = Form.useForm(); |
| 27 | |
| 28 | const fetchActivities = async () => { |
| 29 | setLoading(true); |
| 30 | try { |
| 31 | const data = keyword.trim() |
| 32 | ? await searchActivitiesByTitle(keyword.trim()) |
| 33 | : await getAllActivities(); |
| 34 | setActivities(data.data || []); |
| 35 | } catch (error) { |
| 36 | message.error('获取公告失败'); |
| 37 | } finally { |
| 38 | setLoading(false); |
| 39 | } |
| 40 | }; |
| 41 | |
| 42 | useEffect(() => { |
| 43 | fetchActivities(); |
| 44 | }, []); |
| 45 | |
| 46 | const handleSearch = () => { |
| 47 | fetchActivities(); |
| 48 | }; |
| 49 | |
| 50 | const handleDelete = (activityid) => { |
| 51 | confirm({ |
| 52 | title: '确认删除该公告吗?', |
| 53 | icon: <ExclamationCircleOutlined />, |
| 54 | okText: '删除', |
| 55 | okType: 'danger', |
| 56 | cancelText: '取消', |
| 57 | onOk: async () => { |
| 58 | try { |
| 59 | const res = await deleteActivity(activityid); |
| 60 | if (res.data === true) { |
| 61 | message.success('删除成功'); |
| 62 | fetchActivities(); |
| 63 | } else { |
| 64 | message.error('删除失败'); |
| 65 | } |
| 66 | } catch { |
| 67 | message.error('删除请求失败'); |
| 68 | } |
| 69 | }, |
| 70 | }); |
| 71 | }; |
| 72 | |
| 73 | const openEditModal = (record) => { |
| 74 | setEditMode(true); |
| 75 | setCurrentActivity(record); |
| 76 | form.setFieldsValue({ |
| 77 | activityid: record.activityid, |
| 78 | title: record.title, |
| 79 | content: record.content, |
| 80 | isShow: record.is_show, |
| 81 | }); |
| 82 | setModalVisible(true); |
| 83 | }; |
| 84 | |
| 85 | const openCreateModal = () => { |
| 86 | setEditMode(false); |
| 87 | form.resetFields(); |
| 88 | setModalVisible(true); |
| 89 | }; |
| 90 | |
| 91 | const handleModalOk = async () => { |
| 92 | try { |
| 93 | const values = await form.validateFields(); |
| 94 | const formData = new FormData(); |
| 95 | Object.keys(values).forEach(key => { |
| 96 | if (key !== 'photo' && values[key] !== undefined) { |
| 97 | formData.append(key, values[key]); |
| 98 | } |
| 99 | }); |
| 100 | if (values.photo && values.photo.file) { |
| 101 | formData.append('photo', values.photo.file); |
| 102 | } |
| 103 | |
| 104 | const res = editMode |
| 105 | ? await updateActivity(formData) |
| 106 | : await createActivity(formData); |
| 107 | |
| 108 | if (res.data === true) { |
| 109 | message.success(editMode ? '修改成功' : '创建成功'); |
| 110 | setModalVisible(false); |
| 111 | fetchActivities(); |
| 112 | } else { |
| 113 | message.error('提交失败'); |
| 114 | } |
| 115 | } catch (error) { |
| 116 | message.error('提交失败,请检查表单'); |
| 117 | } |
| 118 | }; |
| 119 | |
| 120 | const columns = [ |
| 121 | { |
| 122 | title: 'ID', |
| 123 | dataIndex: 'activityid', |
| 124 | key: 'activityid', |
| 125 | width: 80, |
| 126 | }, |
| 127 | { |
| 128 | title: '标题', |
| 129 | dataIndex: 'title', |
| 130 | key: 'title', |
| 131 | width: 200, |
| 132 | ellipsis: true, |
| 133 | }, |
| 134 | { |
| 135 | title: '内容', |
| 136 | dataIndex: 'content', |
| 137 | key: 'content', |
| 138 | ellipsis: { showTitle: false }, |
| 139 | render: (text) => ( |
| 140 | <Tooltip placement="topLeft" title={text}> |
| 141 | {text} |
| 142 | </Tooltip> |
| 143 | ), |
| 144 | }, |
| 145 | { |
| 146 | title: '图片', |
| 147 | dataIndex: 'photo', |
| 148 | key: 'photo', |
| 149 | width: 100, |
| 150 | render: (url) => |
| 151 | url ? ( |
| 152 | <Image |
| 153 | src={`http://localhost:8080${url}`} |
| 154 | width={80} |
| 155 | height={80} |
| 156 | style={{ objectFit: 'cover' }} |
| 157 | /> |
| 158 | ) : ( |
| 159 | <Tag color="default">无</Tag> |
| 160 | ), |
| 161 | }, |
| 162 | { |
| 163 | title: '展示状态', |
| 164 | dataIndex: 'is_show', |
| 165 | key: 'is_show', |
| 166 | width: 100, |
| 167 | render: (val) => |
| 168 | val === 0 ? <Tag color="green">展示</Tag> : <Tag color="red">隐藏</Tag>, |
| 169 | }, |
| 170 | { |
| 171 | title: '发布时间', |
| 172 | dataIndex: 'time', |
| 173 | key: 'time', |
| 174 | width: 180, |
| 175 | render: (time) => |
| 176 | time ? new Date(time).toLocaleString() : <Tag color="default">暂无</Tag>, |
| 177 | }, |
| 178 | { |
| 179 | title: '操作', |
| 180 | key: 'action', |
| 181 | width: 180, |
| 182 | render: (_, record) => ( |
| 183 | <Space size="middle"> |
| 184 | <Button type="primary" onClick={() => openEditModal(record)}> |
| 185 | 修改 |
| 186 | </Button> |
| 187 | <Button danger onClick={() => handleDelete(record.activityid)}> |
| 188 | 删除 |
| 189 | </Button> |
| 190 | </Space> |
| 191 | ), |
| 192 | }, |
| 193 | ]; |
| 194 | |
| 195 | return ( |
| 196 | <div style={{ padding: 20 }}> |
| 197 | <h2 style={{ marginBottom: 20 }}>公告管理面板</h2> |
| 198 | <Space style={{ marginBottom: 16 }}> |
| 199 | <Input |
| 200 | placeholder="请输入标题关键词" |
| 201 | value={keyword} |
| 202 | onChange={(e) => setKeyword(e.target.value)} |
| 203 | style={{ width: 300 }} |
| 204 | /> |
| 205 | <Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}> |
| 206 | 搜索 |
| 207 | </Button> |
| 208 | <Button onClick={() => { setKeyword(''); fetchActivities(); }}> |
| 209 | 重置 |
| 210 | </Button> |
| 211 | <Button type="primary" onClick={openCreateModal}> |
| 212 | 新建公告 |
| 213 | </Button> |
| 214 | </Space> |
| 215 | <Table |
| 216 | rowKey="activityid" |
| 217 | columns={columns} |
| 218 | dataSource={activities} |
| 219 | loading={loading} |
| 220 | scroll={{ x: 1200 }} |
| 221 | pagination={{ pageSize: 8 }} |
| 222 | bordered |
| 223 | size="middle" |
| 224 | /> |
| 225 | |
| 226 | <Modal |
| 227 | title={editMode ? '修改公告' : '新建公告'} |
| 228 | open={modalVisible} |
| 229 | onOk={handleModalOk} |
| 230 | onCancel={() => setModalVisible(false)} |
| 231 | okText="提交" |
| 232 | cancelText="取消" |
| 233 | destroyOnClose |
| 234 | > |
| 235 | <Form form={form} layout="vertical" preserve={false}> |
| 236 | {editMode && ( |
| 237 | <Form.Item name="activityid" hidden> |
| 238 | <Input /> |
| 239 | </Form.Item> |
| 240 | )} |
| 241 | <Form.Item |
| 242 | name="title" |
| 243 | label="标题" |
| 244 | rules={[{ required: true, message: '请输入标题' }]} |
| 245 | > |
| 246 | <Input /> |
| 247 | </Form.Item> |
| 248 | <Form.Item |
| 249 | name="content" |
| 250 | label="内容" |
| 251 | rules={[{ required: true, message: '请输入内容' }]} |
| 252 | > |
| 253 | <Input.TextArea rows={4} /> |
| 254 | </Form.Item> |
| 255 | <Form.Item name="isShow" label="是否展示"> |
| 256 | <Input placeholder="0 表示展示,1 表示隐藏" /> |
| 257 | </Form.Item> |
| 258 | <Form.Item name="photo" label="上传图片"> |
| 259 | <Upload beforeUpload={() => false} maxCount={1}> |
| 260 | <Button icon={<UploadOutlined />}>选择文件</Button> |
| 261 | </Upload> |
| 262 | </Form.Item> |
| 263 | </Form> |
| 264 | </Modal> |
| 265 | </div> |
| 266 | ); |
| 267 | }; |
| 268 | |
| 269 | export default ActivityAdminPanel; |