feat(ui): 专业风UI改造 主色#275354 + Brand系统标题统一
Deploy to Server / deploy (push) Canceled after 14m40s

- 全量替换游戏感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:
Trae Assistant
2026-10-04 23:05:39 +08:00
parent b4b0f8b76d
commit 1a51a854a3
21 changed files with 333 additions and 290 deletions
+30 -25
View File
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import Button from '../../components/Button';
import DataTable from '../../components/DataTable';
import Brand from '../../components/Brand';
import { usersApi, errMsg } from '../../api';
import { formatDateRange } from '../../utils/export';
import type { UserDetail } from '../../types';
@@ -31,54 +32,58 @@ export default function UserDashboard() {
if (!data) return null;
return (
<div className="min-h-screen bg-gray-50 px-4 py-6 md:py-10">
<div className="min-h-screen bg-paper px-4 py-6 md:py-10">
<div className="max-w-5xl mx-auto">
<div className="flex justify-between items-center mb-5 flex-wrap gap-2">
<div className="flex items-start gap-2">
<IconClock size={28} className="mt-0.5 shrink-0" />
<h1 className="text-xl md:text-2xl font-black tracking-wide">个人义工时长总览</h1>
</div>
<div className="mb-5 flex items-center justify-between flex-wrap gap-2">
<Brand variant="dark" size="sm" showTitle={true} />
<Button variant="secondary" onClick={() => navigate('/u')}><span className="inline-flex items-center gap-2"><IconArrowLeft size={18} />返回查询</span></Button>
</div>
<div className="bg-white border-2 border-black p-5 mb-5">
<div className="mb-5 flex items-start gap-2 animate-fade-in-up">
<div className="shrink-0 p-2 bg-primary-50 text-primary-600 rounded-md mt-0.5">
<IconClock size={26} />
</div>
<h1 className="section-title mt-1">个人义工时长总览</h1>
</div>
<div className="card p-5 mb-5 animate-fade-in-up">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<div className="text-xs text-gray-500 mb-1">姓名</div>
<div className="text-lg font-bold">{data.name}</div>
<div className="muted mb-1">姓名</div>
<div className="text-lg font-semibold text-gray-800">{data.name}</div>
</div>
<div>
<div className="text-xs text-gray-500 mb-1">班级</div>
<div className="text-lg font-bold">{data.class || '—'}</div>
<div className="muted mb-1">班级</div>
<div className="text-lg font-semibold text-gray-800">{data.class || '—'}</div>
</div>
<div>
<div className="text-xs text-gray-500 mb-1">义工号</div>
<div className="text-lg font-bold">{data.volunteer_no || '—'}</div>
<div className="muted mb-1">义工号</div>
<div className="text-lg font-semibold text-gray-800">{data.volunteer_no || '—'}</div>
</div>
<div>
<div className="text-xs text-gray-500 mb-1">所属工作组</div>
<div className="text-lg font-bold">{data.group_name || '—'}</div>
<div className="muted mb-1">所属工作组</div>
<div className="text-lg font-semibold text-gray-800">{data.group_name || '—'}</div>
</div>
</div>
<div className="mt-5 pt-5 border-t-2 border-gray-200 grid grid-cols-2 gap-4">
<div className="bg-gray-50 border-2 border-gray-200 p-4">
<div className="text-xs text-gray-500 mb-1">累计义工时长</div>
<div className="text-2xl md:text-3xl font-black text-black">{Number(data.total_hours || 0).toFixed(2)} <span className="text-base font-bold text-gray-600">小时</span></div>
<div className="mt-5 pt-5 border-t border-gray-200 grid grid-cols-2 gap-4">
<div className="card p-4 bg-primary-50/50">
<div className="muted mb-1">累计义工时长</div>
<div className="text-2xl md:text-3xl font-semibold text-primary-600">{Number(data.total_hours || 0).toFixed(2)} <span className="text-base font-medium text-gray-600">小时</span></div>
</div>
<div className="bg-gray-50 border-2 border-gray-200 p-4">
<div className="text-xs text-gray-500 mb-1">参与活动数</div>
<div className="text-2xl md:text-3xl font-black text-black">{data.activities.length} <span className="text-base font-bold text-gray-600">次</span></div>
<div className="card p-4">
<div className="muted mb-1">参与活动数</div>
<div className="text-2xl md:text-3xl font-semibold text-gray-800">{data.activities.length} <span className="text-base font-medium text-gray-600">次</span></div>
</div>
</div>
</div>
<h2 className="font-bold text-lg mb-3">参与活动明细</h2>
<h2 className="font-semibold text-lg mb-3 text-gray-800">参与活动明细</h2>
<DataTable
id="user-activities-table"
columns={[
{ key: 'title', title: '活动名称', render: (r: any) => <span className="font-medium">{r.title}</span> },
{ key: 'title', title: '活动名称', render: (r: any) => <span className="font-medium text-gray-800">{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)}</span> },
{ key: 'hours', title: '时长(小时)', render: (r: any) => <span className="font-semibold text-primary-600">{Number(r.hours || 0).toFixed(2)}</span> },
{ key: 'remark', title: '备注', render: (r: any) => r.remark || '—' },
]}
data={data.activities}