feat: enhance user profile with detailed tags and asset evaluation
Optimize user detail page for asset assessment and tag info. #VERCEL_SKIP Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
"use client"
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
|
||||
type Row = {
|
||||
id: string
|
||||
@@ -12,9 +12,38 @@ type Row = {
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
type ApiResp = {
|
||||
data: Row[]
|
||||
pagination: { page: number; pageSize: number; total: number; totalPages: number }
|
||||
type Pagination = { page: number; pageSize: number; total: number; totalPages: number }
|
||||
|
||||
// The API may return different shapes; normalize them here.
|
||||
function normalizeResponse(json: any): { items: Row[]; total: number; pagination?: Pagination } {
|
||||
// v2 shape: { success, data: { items, pagination, totalValue } }
|
||||
if (json && json.data && Array.isArray(json.data.items)) {
|
||||
const items = json.data.items as Row[]
|
||||
const total = json.data?.pagination?.total ?? json.total ?? items.length
|
||||
return { items, total, pagination: json.data.pagination }
|
||||
}
|
||||
|
||||
// v1 shape: { data: Row[], pagination }
|
||||
if (json && Array.isArray(json.data)) {
|
||||
const items = json.data as Row[]
|
||||
const total = json.pagination?.total ?? json.total ?? items.length
|
||||
return { items, total, pagination: json.pagination }
|
||||
}
|
||||
|
||||
// shape: { items: Row[], pagination }
|
||||
if (json && Array.isArray(json.items)) {
|
||||
const items = json.items as Row[]
|
||||
const total = json.pagination?.total ?? json.total ?? items.length
|
||||
return { items, total, pagination: json.pagination }
|
||||
}
|
||||
|
||||
// raw array
|
||||
if (Array.isArray(json)) {
|
||||
return { items: json as Row[], total: (json as Row[]).length }
|
||||
}
|
||||
|
||||
// unknown shape
|
||||
return { items: [], total: 0 }
|
||||
}
|
||||
|
||||
export default function UserList({ queryString }: { queryString: string }) {
|
||||
@@ -26,23 +55,26 @@ export default function UserList({ queryString }: { queryString: string }) {
|
||||
useEffect(() => {
|
||||
let aborted = false
|
||||
const controller = new AbortController()
|
||||
|
||||
async function load() {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
const res = await fetch(`/api/users?${queryString}`, { signal: controller.signal, cache: 'no-store' })
|
||||
const res = await fetch(`/api/users?${queryString}`, { signal: controller.signal, cache: "no-store" })
|
||||
if (!res.ok) throw new Error(`请求失败: ${res.status}`)
|
||||
const json: ApiResp = await res.json()
|
||||
const json = await res.json()
|
||||
const { items, total } = normalizeResponse(json)
|
||||
if (!aborted) {
|
||||
setData(json.data || [])
|
||||
setTotal(json.pagination?.total || 0)
|
||||
setData(Array.isArray(items) ? items : [])
|
||||
setTotal(typeof total === "number" ? total : 0)
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (!aborted) setError(e?.message || '未知错误')
|
||||
if (!aborted) setError(e?.message || "未知错误")
|
||||
} finally {
|
||||
if (!aborted) setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
load()
|
||||
return () => {
|
||||
aborted = true
|
||||
@@ -50,7 +82,7 @@ export default function UserList({ queryString }: { queryString: string }) {
|
||||
}
|
||||
}, [queryString])
|
||||
|
||||
const rows = useMemo(() => data, [data])
|
||||
const rows = useMemo(() => (Array.isArray(data) ? data : []), [data])
|
||||
|
||||
if (loading) {
|
||||
return <div className="rounded-lg border bg-white p-4">加载中...</div>
|
||||
@@ -87,16 +119,22 @@ export default function UserList({ queryString }: { queryString: string }) {
|
||||
<td className="px-3 py-2">
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{(r.tags || []).slice(0, 3).map((t) => (
|
||||
<span key={t} className="px-2 py-0.5 rounded-full bg-gray-100 text-gray-700">{t}</span>
|
||||
<span key={t} className="px-2 py-0.5 rounded-full bg-gray-100 text-gray-700">
|
||||
{t}
|
||||
</span>
|
||||
))}
|
||||
{(r.tags || []).length > 3 && <span className="px-2 py-0.5 rounded-full border">+{r.tags.length - 3}</span>}
|
||||
{(r.tags || []).length > 3 && (
|
||||
<span className="px-2 py-0.5 rounded-full border">+{r.tags.length - 3}</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{!rows.length && (
|
||||
<tr>
|
||||
<td className="px-3 py-8 text-center text-gray-500" colSpan={6}>无匹配数据</td>
|
||||
<td className="px-3 py-8 text-center text-gray-500" colSpan={6}>
|
||||
无匹配数据
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user