- 全量替换游戏感Neo-Brutalist为政务专业风:圆角2-16px、薄灰边、柔阴影 - Tailwind主题:primary 10阶调色板 + Noto Sans SC主字体 + fade-in-up动画 - 全局CSS:浅灰F6F8F9背景 + 主色焦点环和滚动条 + card/chip工具类 - 品牌组件Brand.tsx:云图形截断防云盘旧字泄露 + 新标题组合 - 6基础组件 + 8页面:Layout深绿侧栏 Login卡片 Export去五色卡 Activities/Hours/Users 用户端Index/Dashboard - 数据链路回归:u/2张三2h明细正确显示 - tsc无错误 vite build成功
This commit is contained in:
@@ -69,14 +69,16 @@ export default function Users() {
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-5 flex items-start gap-2">
|
||||
<IconUsers size={28} className="mt-0.5 shrink-0" />
|
||||
<div className="shrink-0 p-2 bg-primary-50 text-primary-600 rounded-md mt-0.5">
|
||||
<IconUsers size={26} />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-xl md:text-2xl font-black mb-1">用户管理</h1>
|
||||
<p className="text-sm text-gray-500">管理部员信息,查看个人累计义工时长与参与活动明细</p>
|
||||
<h1 className="section-title">用户管理</h1>
|
||||
<p className="muted mt-1">管理部员信息,查看个人累计义工时长与参与活动明细</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white border-2 border-black p-4 mb-4 flex flex-col md:flex-row gap-3 md:items-center md:justify-between">
|
||||
<div className="card p-4 mb-4 flex flex-col md:flex-row gap-3 md:items-center md:justify-between">
|
||||
<Input placeholder="搜索 姓名/班级/义工号/工作组..." value={keyword} onChange={(e) => setKeyword(e.target.value)} className="md:max-w-md" />
|
||||
<Button onClick={openNew}><span className="inline-flex items-center gap-2"><IconPlus size={18} />新增部员</span></Button>
|
||||
</div>
|
||||
@@ -84,12 +86,12 @@ export default function Users() {
|
||||
<DataTable<UserItem>
|
||||
columns={[
|
||||
{ key: 'class', title: '班级', width: '120px', render: (r) => <span className="whitespace-nowrap">{r.class}</span> },
|
||||
{ key: 'name', title: '姓名', render: (r) => <span className="font-bold">{r.name}</span> },
|
||||
{ key: 'name', title: '姓名', render: (r) => <span className="font-semibold">{r.name}</span> },
|
||||
{ key: 'volunteer_no', title: '义工号', render: (r) => r.volunteer_no || '—' },
|
||||
{ key: 'group_name', title: '所属工作组', render: (r) => r.group_name || '—' },
|
||||
{ key: 'total_hours', title: '累计时长(小时)', render: (r) => <span className="font-black">{Number(r.total_hours || 0).toFixed(2)}</span> },
|
||||
{ key: 'total_hours', title: '累计时长(小时)', render: (r) => <span className="font-semibold text-primary-600">{Number(r.total_hours || 0).toFixed(2)}</span> },
|
||||
{
|
||||
key: 'op', title: '操作', width: '220px', render: (r) => (
|
||||
key: 'op', title: '操作', width: '240px', render: (r) => (
|
||||
<div className="flex gap-1.5 flex-wrap">
|
||||
<Button size="sm" variant="secondary" onClick={() => openDetail(r)}><span className="inline-flex items-center gap-1.5"><IconEye size={14} />详情</span></Button>
|
||||
<Button size="sm" variant="secondary" onClick={() => openEdit(r)}><span className="inline-flex items-center gap-1.5"><IconEdit size={14} />编辑</span></Button>
|
||||
@@ -106,17 +108,17 @@ export default function Users() {
|
||||
footer={<><Button variant="secondary" onClick={() => setModalOpen(false)}>取消</Button><Button onClick={submit}>{editId ? '保存修改' : '添加部员'}</Button></>}>
|
||||
<div className="space-y-4 max-w-md">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div><label className="block text-sm font-bold mb-1.5">班级 *</label>
|
||||
<div><label className="block text-sm font-semibold mb-1.5">班级 *</label>
|
||||
<Input value={form.class} onChange={(e) => setForm({ ...form, class: e.target.value })} placeholder="如:高三(1)班" />
|
||||
</div>
|
||||
<div><label className="block text-sm font-bold mb-1.5">姓名 *</label>
|
||||
<div><label className="block text-sm font-semibold mb-1.5">姓名 *</label>
|
||||
<Input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
|
||||
</div>
|
||||
</div>
|
||||
<div><label className="block text-sm font-bold mb-1.5">义工号</label>
|
||||
<div><label className="block text-sm font-semibold mb-1.5">义工号</label>
|
||||
<Input value={form.volunteer_no} onChange={(e) => setForm({ ...form, volunteer_no: e.target.value })} />
|
||||
</div>
|
||||
<div><label className="block text-sm font-bold mb-1.5">所属工作组</label>
|
||||
<div><label className="block text-sm font-semibold mb-1.5">所属工作组</label>
|
||||
<Select value={String(form.group_id || '')} onChange={(e) => setForm({ ...form, group_id: e.target.value ? Number(e.target.value) : null })}>
|
||||
<option value="">-- 不设置 --</option>
|
||||
{groups.map(g => <option key={g.id} value={g.id}>{g.name}</option>)}
|
||||
@@ -130,26 +132,26 @@ export default function Users() {
|
||||
footer={<Button onClick={() => setDetailOpen(false)}>关闭</Button>}>
|
||||
{detail && (
|
||||
<div className="space-y-5">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="bg-gray-50 border-2 border-gray-200 p-3">
|
||||
<div className="text-xs text-gray-500 mb-1">姓名</div><div className="font-black text-lg">{detail.name}</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
<div className="card p-3">
|
||||
<div className="muted mb-1">姓名</div><div className="font-semibold text-lg text-gray-800">{detail.name}</div>
|
||||
</div>
|
||||
<div className="bg-gray-50 border-2 border-gray-200 p-3">
|
||||
<div className="text-xs text-gray-500 mb-1">班级</div><div className="font-bold">{detail.class}</div>
|
||||
<div className="card p-3">
|
||||
<div className="muted mb-1">班级</div><div className="font-semibold text-gray-800">{detail.class}</div>
|
||||
</div>
|
||||
<div className="bg-gray-50 border-2 border-gray-200 p-3">
|
||||
<div className="text-xs text-gray-500 mb-1">义工号</div><div className="font-bold">{detail.volunteer_no || '—'}</div>
|
||||
<div className="card p-3">
|
||||
<div className="muted mb-1">义工号</div><div className="font-semibold text-gray-800">{detail.volunteer_no || '—'}</div>
|
||||
</div>
|
||||
<div className="bg-gray-50 border-2 border-gray-200 p-3">
|
||||
<div className="text-xs text-gray-500 mb-1">累计时长</div><div className="font-black text-lg text-black">{Number(detail.total_hours || 0).toFixed(2)}h</div>
|
||||
<div className="card p-3 bg-primary-50/60">
|
||||
<div className="muted mb-1">累计时长</div><div className="font-semibold text-lg text-primary-600">{Number(detail.total_hours || 0).toFixed(2)}h</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold mb-2">参与的活动({detail.activities.length} 次)</h3>
|
||||
<h3 className="font-semibold mb-2 text-gray-800">参与的活动({detail.activities.length} 次)</h3>
|
||||
<DataTable columns={[
|
||||
{ key: 'title', title: '活动名称', render: (r: any) => <span className="font-medium">{r.title}</span> },
|
||||
{ key: 'time', title: '时间', render: (r: any) => formatDateRange(r.start_time, r.end_time) },
|
||||
{ key: 'hours', title: '时长', render: (r: any) => <span className="font-bold">{Number(r.hours || 0).toFixed(2)}h</span> },
|
||||
{ key: 'hours', title: '时长', render: (r: any) => <span className="font-semibold text-primary-600">{Number(r.hours || 0).toFixed(2)}h</span> },
|
||||
{ key: 'remark', title: '备注', render: (r: any) => r.remark || '—' },
|
||||
]} data={detail.activities} rowKey={(r: any) => r.id} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user