- 全量替换游戏感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:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user