21301050 | 43969c9 | 2025-06-09 23:15:40 +0800 | [diff] [blame^] | 1 | import React, { useState, useEffect } from 'react'; |
| 2 | import { |
| 3 | Table, |
| 4 | Button, |
| 5 | Modal, |
| 6 | Image, |
| 7 | message, |
| 8 | Spin, |
| 9 | Input, |
| 10 | Select, |
| 11 | Pagination, |
| 12 | Space |
| 13 | } from 'antd'; |
| 14 | import { ExclamationCircleOutlined } from '@ant-design/icons'; |
| 15 | import axios from 'axios'; |
| 16 | |
| 17 | const { confirm } = Modal; |
| 18 | const { Option } = Select; |
| 19 | |
| 20 | const UserManagement = () => { |
| 21 | // 状态管理 |
| 22 | const [users, setUsers] = useState([]); |
| 23 | const [isLoading, setIsLoading] = useState(false); |
| 24 | const [error, setError] = useState(null); |
| 25 | const [currentUserId, setCurrentUserId] = useState(null); |
| 26 | const [applyExamLoading, setApplyExamLoading] = useState(false); |
| 27 | const [searchKeyword, setSearchKeyword] = useState(''); |
| 28 | const [currentPage, setCurrentPage] = useState(1); |
| 29 | const [pageSize, setPageSize] = useState(10); |
| 30 | |
| 31 | // 获取当前用户ID |
| 32 | useEffect(() => { |
| 33 | const userId = 1; // 示例,实际从认证系统获取 |
| 34 | setCurrentUserId(userId ? parseInt(userId) : null); |
| 35 | }, []); |
| 36 | |
| 37 | // 获取所有用户数据 |
| 38 | useEffect(() => { |
| 39 | fetchAllUsers(); |
| 40 | }, [searchKeyword]); |
| 41 | |
| 42 | // 获取所有用户的函数 |
| 43 | const fetchAllUsers = async () => { |
| 44 | setIsLoading(true); |
| 45 | setError(null); |
| 46 | try { |
| 47 | const res = await axios.get('http://localhost:8080/user/alluser'); |
| 48 | setUsers(res.data.data); |
| 49 | setCurrentPage(1); // 重置为第一页 |
| 50 | } catch (err) { |
| 51 | console.error('获取用户失败', err); |
| 52 | setError('获取用户列表失败,请稍后重试'); |
| 53 | message.error('获取用户列表失败'); |
| 54 | } finally { |
| 55 | setIsLoading(false); |
| 56 | } |
| 57 | }; |
| 58 | console.log('当前用户列表:', users); |
| 59 | |
| 60 | // 删除用户 |
| 61 | const handleDeleteUser = async (username) => { |
| 62 | confirm({ |
| 63 | title: '确认删除', |
| 64 | icon: <ExclamationCircleOutlined />, |
| 65 | content: '确定要删除这个用户吗?此操作不可恢复!', |
| 66 | onOk: async () => { |
| 67 | try { |
| 68 | await axios.delete(`http://localhost:8080/user/DeleteUser`, { |
| 69 | params: { username: username } |
| 70 | }); |
| 71 | setUsers(users.filter(user => user.username !== username)); |
| 72 | message.success('用户删除成功'); |
| 73 | } catch (err) { |
| 74 | console.error('用户删除失败', err); |
| 75 | if (err.response && err.response.status === 403) { |
| 76 | message.error('无权删除此用户'); |
| 77 | } else { |
| 78 | message.error('删除用户失败'); |
| 79 | } |
| 80 | } |
| 81 | } |
| 82 | }); |
| 83 | }; |
| 84 | |
| 85 | // 搜索种子 |
| 86 | const handleSearch = async () => { |
| 87 | if (!searchKeyword.trim()) { |
| 88 | fetchAllUsers(); |
| 89 | return; |
| 90 | } |
| 91 | |
| 92 | setIsLoading(true); |
| 93 | setError(null); |
| 94 | try { |
| 95 | const res = await axios.get(`http://localhost:8080/user/finduser`, { |
| 96 | params: { keyword: searchKeyword }, |
| 97 | }); |
| 98 | setUsers(res.data.data); |
| 99 | setCurrentPage(1); // 搜索后重置为第一页 |
| 100 | } catch (err) { |
| 101 | console.error('搜索失败', err); |
| 102 | setError('搜索失败,请稍后重试'); |
| 103 | message.error('搜索失败'); |
| 104 | } finally { |
| 105 | setIsLoading(false); |
| 106 | } |
| 107 | }; |
| 108 | |
| 109 | // 月度下载量考核 |
| 110 | const handleMonthExam = async () => { |
| 111 | if (!currentUserId) { |
| 112 | message.warning('请先登录'); |
| 113 | return; |
| 114 | } |
| 115 | |
| 116 | setApplyExamLoading(true); |
| 117 | try { |
| 118 | const now = new Date(); |
| 119 | const year = now.getFullYear(); |
| 120 | const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始,补零 |
| 121 | const day = String(now.getDate()).padStart(2, '0'); // 日期补零 |
| 122 | const formattedDate = `${year}-${month}-${day}`; |
| 123 | |
| 124 | const currentDate = new Date(); |
| 125 | const lastMonthDate = new Date( |
| 126 | currentDate.getFullYear(), |
| 127 | currentDate.getMonth() - 1, // 月份减1 |
| 128 | currentDate.getDate() // 保持相同的日 |
| 129 | ); |
| 130 | |
| 131 | if (lastMonthDate.getDate() !== currentDate.getDate()) { |
| 132 | lastMonthDate.setDate(0); // 设置为上个月的最后一天 |
| 133 | } |
| 134 | const year1 = lastMonthDate.getFullYear(); |
| 135 | const month1 = String(lastMonthDate.getMonth() + 1).padStart(2, '0'); |
| 136 | const day1 = String(lastMonthDate.getDate()).padStart(2, '0'); |
| 137 | const formattedDate1 = `${year1}-${month1}-${day1}`; |
| 138 | |
| 139 | |
| 140 | |
| 141 | const res = await axios.post('http://localhost:8080/exam/MonthDownload', null, { |
| 142 | params: { startDate: formattedDate1,endDate: formattedDate } |
| 143 | }); |
| 144 | |
| 145 | if (res.data.success) { |
| 146 | message.success(res.data.message); |
| 147 | fetchAllUsers(); // 刷新种子列表 |
| 148 | } |
| 149 | } catch (err) { |
| 150 | console.error('月度考核失败', err); |
| 151 | if (err.response && err.response.status === 403) { |
| 152 | message.error('无权执行此操作'); |
| 153 | } else { |
| 154 | message.error('月度考核失败'); |
| 155 | } |
| 156 | } finally { |
| 157 | setApplyExamLoading(false); |
| 158 | } |
| 159 | }; |
| 160 | |
| 161 | // 季度上传量考核 |
| 162 | const handleQuarterExam = async () => { |
| 163 | if (!currentUserId) { |
| 164 | message.warning('请先登录'); |
| 165 | return; |
| 166 | } |
| 167 | |
| 168 | setApplyExamLoading(true); |
| 169 | try { |
| 170 | const now = new Date(); |
| 171 | const year = now.getFullYear(); |
| 172 | const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始,补零 |
| 173 | const day = String(now.getDate()).padStart(2, '0'); // 日期补零 |
| 174 | const formattedDate = `${year}-${month}-${day}`; |
| 175 | |
| 176 | const currentDate = new Date(); |
| 177 | |
| 178 | const lastMonthDate = new Date( |
| 179 | currentDate.getFullYear(), |
| 180 | currentDate.getMonth() - 3, // 月份减1 |
| 181 | currentDate.getDate() // 保持相同的日 |
| 182 | ); |
| 183 | |
| 184 | if (lastMonthDate.getDate() !== currentDate.getDate()) { |
| 185 | lastMonthDate.setDate(0); // 设置为上个月的最后一天 |
| 186 | } |
| 187 | const year1 = lastMonthDate.getFullYear(); |
| 188 | const month1 = String(lastMonthDate.getMonth() + 1).padStart(2, '0'); |
| 189 | const day1 = String(lastMonthDate.getDate()).padStart(2, '0'); |
| 190 | const formattedDate1 = `${year1}-${month1}-${day1}`; |
| 191 | |
| 192 | |
| 193 | |
| 194 | const res = await axios.post('http://localhost:8080/exam/QuarterUpload', null, { |
| 195 | params: { startDate: formattedDate1,endDate: formattedDate } |
| 196 | }); |
| 197 | |
| 198 | if (res.data.success) { |
| 199 | message.success(res.data.message); |
| 200 | fetchAllUsers(); // 刷新种子列表 |
| 201 | } |
| 202 | } catch (err) { |
| 203 | console.error('季度考核失败', err); |
| 204 | if (err.response && err.response.status === 403) { |
| 205 | message.error('无权执行此操作'); |
| 206 | } else { |
| 207 | message.error('月度考核失败'); |
| 208 | } |
| 209 | } finally { |
| 210 | setApplyExamLoading(false); |
| 211 | } |
| 212 | }; |
| 213 | |
| 214 | // 分页数据计算 |
| 215 | const getCurrentPageData = () => { |
| 216 | const start = (currentPage - 1) * pageSize; |
| 217 | const end = start + pageSize; |
| 218 | return users;//.slice(start, end); |
| 219 | }; |
| 220 | |
| 221 | // 页码变化处理 |
| 222 | const handlePageChange = (page) => { |
| 223 | setCurrentPage(page); |
| 224 | }; |
| 225 | |
| 226 | // 每页条数变化处理 |
| 227 | const handlePageSizeChange = (current, size) => { |
| 228 | setPageSize(size); |
| 229 | setCurrentPage(1); // 重置为第一页 |
| 230 | }; |
| 231 | |
| 232 | // 格式化文件大小 |
| 233 | const formatFileSize = (bytes) => { |
| 234 | if (bytes === 0) return '0 Bytes'; |
| 235 | const k = 1024; |
| 236 | const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; |
| 237 | const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| 238 | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; |
| 239 | }; |
| 240 | |
| 241 | //////////////////////////////////////////////////////////////////////////// |
| 242 | return ( |
| 243 | <div className="p-4 max-w-7xl mx-auto"> |
| 244 | <h1 className="text-2xl font-bold mb-6">用户管理</h1> |
| 245 | |
| 246 | {/* 搜索框 */} |
| 247 | <div className="mb-4 flex items-center"> |
| 248 | <Input |
| 249 | placeholder="搜索用户..." |
| 250 | value={searchKeyword} |
| 251 | onChange={(e) => setSearchKeyword(e.target.value)} |
| 252 | style={{ width: 300 }} |
| 253 | onPressEnter={handleSearch} |
| 254 | /> |
| 255 | <Button |
| 256 | type="primary" |
| 257 | onClick={handleSearch} |
| 258 | style={{ marginLeft: 8 }} |
| 259 | > |
| 260 | 搜索 |
| 261 | </Button> |
| 262 | </div> |
| 263 | |
| 264 | {/* 右上角按钮 */} |
| 265 | <Space> |
| 266 | <Button |
| 267 | type="primary" |
| 268 | loading={applyExamLoading} |
| 269 | onClick={handleMonthExam} |
| 270 | > |
| 271 | 月度下载量检查 |
| 272 | </Button> |
| 273 | <Button |
| 274 | type="primary" |
| 275 | loading={applyExamLoading} |
| 276 | onClick={handleQuarterExam} |
| 277 | > |
| 278 | 季度上传量检查 |
| 279 | </Button> |
| 280 | </Space> |
| 281 | |
| 282 | {/* 加载状态 */} |
| 283 | {isLoading && <Spin size="large" style={{ display: 'block', margin: '100px auto' }} />} |
| 284 | |
| 285 | {/* 错误提示 */} |
| 286 | {error && <div className="mb-4 p-3 bg-red-100 text-red-700 rounded border border-red-200">{error}</div>} |
| 287 | |
| 288 | {/* 种子列表表格 */} |
| 289 | {!isLoading && !error && ( |
| 290 | <> |
| 291 | <Table |
| 292 | columns={[ |
| 293 | { |
| 294 | title: '用户名', |
| 295 | dataIndex: 'username', |
| 296 | key: 'username' |
| 297 | }, |
| 298 | { |
| 299 | title: '密码', |
| 300 | dataIndex: 'password', |
| 301 | key: 'password' |
| 302 | }, |
| 303 | { |
| 304 | title: '上传量', |
| 305 | dataIndex: 'user_upload', |
| 306 | key: 'user_upload', |
| 307 | render: (size) => formatFileSize(size) |
| 308 | }, |
| 309 | { |
| 310 | title: '下载量', |
| 311 | dataIndex: 'user_download', |
| 312 | key: 'user_download', |
| 313 | render: (size) => formatFileSize(size) |
| 314 | }, |
| 315 | { |
| 316 | title: '保种积分', |
| 317 | dataIndex: 'credit', |
| 318 | key: 'credit', |
| 319 | }, |
| 320 | { |
| 321 | title: '头像', |
| 322 | dataIndex: 'image', |
| 323 | key: 'image', |
| 324 | render: (text) => text ? ( |
| 325 | <Image |
| 326 | src={text} |
| 327 | width={50} |
| 328 | height={50} |
| 329 | preview={{ maskClosable: true }} |
| 330 | /> |
| 331 | ) : ( |
| 332 | <div className="w-16 h-16 bg-gray-200 flex items-center justify-center">无头像</div> |
| 333 | ) |
| 334 | }, |
| 335 | { |
| 336 | title: '性别', |
| 337 | dataIndex: 'sex', |
| 338 | key: 'sex', |
| 339 | }, |
| 340 | { |
| 341 | title: '用户id', |
| 342 | dataIndex: 'userid', |
| 343 | key: 'userid' |
| 344 | }, |
| 345 | { |
| 346 | title: '用户等级', |
| 347 | dataIndex: 'grade_id', |
| 348 | key: 'grade_id' |
| 349 | }, |
| 350 | { |
| 351 | title: 'passkey', |
| 352 | dataIndex: 'passkey', |
| 353 | key: 'passkey' |
| 354 | }, |
| 355 | { |
| 356 | title: '分享率', |
| 357 | dataIndex: 'ratio', |
| 358 | key: 'ratio' |
| 359 | }, |
| 360 | { |
| 361 | title: '年龄', |
| 362 | dataIndex: 'age', |
| 363 | key: 'age' |
| 364 | }, |
| 365 | { |
| 366 | title: '邮箱', |
| 367 | dataIndex: 'email', |
| 368 | key: 'email' |
| 369 | }, |
| 370 | { |
| 371 | title: '用户权限', |
| 372 | dataIndex: 'permission', |
| 373 | key: 'permission' |
| 374 | }, |
| 375 | { |
| 376 | title: '操作', |
| 377 | key: 'action', |
| 378 | render: (_, record) => ( |
| 379 | <Space> |
| 380 | <Button |
| 381 | danger |
| 382 | onClick={() => handleDeleteUser(record.username)} |
| 383 | loading={isLoading} |
| 384 | > |
| 385 | 删除 |
| 386 | </Button> |
| 387 | </Space> |
| 388 | ) |
| 389 | } |
| 390 | ]} |
| 391 | dataSource={getCurrentPageData()} |
| 392 | rowKey="userid" |
| 393 | pagination={false} |
| 394 | loading={isLoading} |
| 395 | /> |
| 396 | |
| 397 | {/* 分页控件 */} |
| 398 | {users.length > 0 && ( |
| 399 | <div style={{ marginTop: 16, textAlign: 'center' }}> |
| 400 | <Pagination |
| 401 | current={currentPage} |
| 402 | pageSize={pageSize} |
| 403 | total={users.length} |
| 404 | onChange={handlePageChange} |
| 405 | onShowSizeChange={handlePageSizeChange} |
| 406 | showSizeChanger |
| 407 | showTotal={(total) => `共 ${total} 条记录`} |
| 408 | pageSizeOptions={['10', '20', '50']} |
| 409 | /> |
| 410 | </div> |
| 411 | )} |
| 412 | </> |
| 413 | )} |
| 414 | </div> |
| 415 | ); |
| 416 | }; |
| 417 | |
| 418 | export default UserManagement; |