import { useEffect, useState } from 'react'; import Button from '../../components/Button'; import Input from '../../components/Input'; import Select from '../../components/Select'; import DataTable from '../../components/DataTable'; import Modal from '../../components/Modal'; import { groupsApi, usersApi, errMsg } from '../../api'; import { formatDateRange } from '../../utils/export'; import type { UserItem, UserDetail, WorkGroup } from '../../types'; import { IconUsers, IconPlus, IconEdit, IconTrash, IconEye } from '../../components/Icons'; type Form = { class: string; name: string; volunteer_no: string; group_id: number | null }; const emptyForm: Form = { class: '', name: '', volunteer_no: '', group_id: null }; export default function Users() { const [list, setList] = useState([]); const [groups, setGroups] = useState([]); const [loading, setLoading] = useState(false); const [keyword, setKeyword] = useState(''); const [modalOpen, setModalOpen] = useState(false); const [form, setForm] = useState
(emptyForm); const [editId, setEditId] = useState(null); const [error, setError] = useState(''); const [detailOpen, setDetailOpen] = useState(false); const [detail, setDetail] = useState(null); const load = async () => { setLoading(true); try { const [u, g] = await Promise.all([usersApi.list(), groupsApi.list()]); setList(u); setGroups(g); } finally { setLoading(false); } }; useEffect(() => { load(); }, []); const filtered = list.filter(u => { if (!keyword.trim()) return true; const k = keyword.trim().toLowerCase(); return [u.name, u.class, u.volunteer_no, u.group_name || ''].some(s => String(s || '').toLowerCase().includes(k)); }); const openNew = () => { setForm(emptyForm); setEditId(null); setError(''); setModalOpen(true); }; const openEdit = (u: UserItem) => { setForm({ class: u.class, name: u.name, volunteer_no: u.volunteer_no || '', group_id: u.group_id }); setEditId(u.id); setError(''); setModalOpen(true); }; const onDelete = (u: UserItem) => { if (!confirm(`确认删除部员「${u.class} ${u.name}」?其所有时长登记也会一并删除。`)) return; usersApi.remove(u.id).then(load).catch(e => alert(errMsg(e))); }; const openDetail = async (u: UserItem) => { try { const d = await usersApi.detail(u.id); setDetail(d); setDetailOpen(true); } catch (e) { alert(errMsg(e)); } }; const submit = async () => { if (!form.class.trim() || !form.name.trim()) { setError('班级与姓名不能为空'); return; } try { const payload = { ...form, volunteer_no: form.volunteer_no.trim() || null, group_id: form.group_id || null }; if (editId) await usersApi.update(editId, payload); else await usersApi.create(payload); setModalOpen(false); load(); } catch (e) { setError(errMsg(e)); } }; return (

用户管理

管理部员信息,查看个人累计义工时长与参与活动明细

setKeyword(e.target.value)} className="md:max-w-md" />
columns={[ { key: 'class', title: '班级', width: '120px', render: (r) => {r.class} }, { key: 'name', title: '姓名', render: (r) => {r.name} }, { key: 'volunteer_no', title: '义工号', render: (r) => r.volunteer_no || '—' }, { key: 'group_name', title: '所属工作组', render: (r) => r.group_name || '—' }, { key: 'total_hours', title: '累计时长(小时)', render: (r) => {Number(r.total_hours || 0).toFixed(2)} }, { key: 'op', title: '操作', width: '240px', render: (r) => (
) } ]} data={filtered} rowKey={(r) => r.id} /> setModalOpen(false)} footer={<>}>
setForm({ ...form, class: e.target.value })} placeholder="如:高三(1)班" />
setForm({ ...form, name: e.target.value })} />
setForm({ ...form, volunteer_no: e.target.value })} />
{error &&
{error}
}
setDetailOpen(false)} width="max-w-3xl" footer={}> {detail && (
姓名
{detail.name}
班级
{detail.class}
义工号
{detail.volunteer_no || '—'}
累计时长
{Number(detail.total_hours || 0).toFixed(2)}h

参与的活动({detail.activities.length} 次)

{r.title} }, { key: 'time', title: '时间', render: (r: any) => formatDateRange(r.start_time, r.end_time) }, { key: 'hours', title: '时长', render: (r: any) => {Number(r.hours || 0).toFixed(2)}h }, { key: 'remark', title: '备注', render: (r: any) => r.remark || '—' }, ]} data={detail.activities} rowKey={(r: any) => r.id} />
)}
); }