feat:前端修改(我认为ok)
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { adminApi, errMessage, type FundProduct } from "@/lib/admin-api";
|
||||
import { isAdvisor } from "@/lib/roles";
|
||||
import type { UserPayload } from "@/types/api";
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
EmptyState,
|
||||
ErrorState,
|
||||
LoadingBlock,
|
||||
Pagination,
|
||||
SearchInput,
|
||||
Select,
|
||||
TableWrap,
|
||||
Td,
|
||||
Th,
|
||||
statusTone,
|
||||
} from "@/components/admin/ui";
|
||||
|
||||
const RISK_OPTIONS = [
|
||||
{ value: "", label: "全部风险等级" },
|
||||
{ value: "低风险", label: "低风险" },
|
||||
{ value: "中低风险", label: "中低风险" },
|
||||
{ value: "中风险", label: "中风险" },
|
||||
{ value: "中高风险", label: "中高风险" },
|
||||
{ value: "高风险", label: "高风险" },
|
||||
];
|
||||
|
||||
export function FundsSection({ user }: { user: UserPayload }) {
|
||||
const advisor = isAdvisor(user);
|
||||
const [items, setItems] = useState<FundProduct[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [totalPages, setTotalPages] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
const [keyword, setKeyword] = useState("");
|
||||
const [riskLevel, setRiskLevel] = useState("");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
try {
|
||||
const result = advisor
|
||||
? await adminApi.funds({ keyword: keyword || undefined, risk_level: riskLevel || undefined, page, page_size: 10 })
|
||||
: await adminApi.products({ keyword: keyword || undefined, page, page_size: 10 });
|
||||
setItems(result.items ?? []);
|
||||
setTotal(result.total ?? 0);
|
||||
setTotalPages(result.total_pages ?? 0);
|
||||
} catch (e) {
|
||||
setError(errMessage(e, "基金数据加载失败"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [advisor, keyword, riskLevel, page]);
|
||||
|
||||
useEffect(() => {
|
||||
void load();
|
||||
}, [load]);
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="基金数据"
|
||||
description={advisor ? "白名单在售产品" : "全部在售基金产品"}
|
||||
bodyClassName="p-0"
|
||||
actions={
|
||||
<Link href="/products" className="text-sm font-medium text-indigo-600 hover:underline">
|
||||
在官网查看
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-wrap items-center gap-3 border-b border-slate-100 p-4">
|
||||
<SearchInput
|
||||
className="w-64"
|
||||
placeholder="搜索基金名称或代码"
|
||||
value={keyword}
|
||||
onChange={(event) => {
|
||||
setKeyword(event.target.value);
|
||||
setPage(1);
|
||||
}}
|
||||
/>
|
||||
{advisor && (
|
||||
<Select value={riskLevel} onChange={(event) => { setRiskLevel(event.target.value); setPage(1); }}>
|
||||
{RISK_OPTIONS.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
<Button size="sm" className="ml-auto" onClick={() => void load()} loading={loading}>
|
||||
查询
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<LoadingBlock />
|
||||
) : error ? (
|
||||
<ErrorState message={error} onRetry={() => void load()} />
|
||||
) : items.length === 0 ? (
|
||||
<EmptyState text="没有匹配的基金产品" />
|
||||
) : (
|
||||
<>
|
||||
<TableWrap>
|
||||
<thead>
|
||||
<tr>
|
||||
<Th>基金代码</Th>
|
||||
<Th>基金名称</Th>
|
||||
<Th>类型</Th>
|
||||
<Th>风险等级</Th>
|
||||
<Th className="text-right">最新净值</Th>
|
||||
<Th>净值日期</Th>
|
||||
<Th>基金经理</Th>
|
||||
<Th>状态</Th>
|
||||
<Th className="text-right">操作</Th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-50">
|
||||
{items.map((fund) => (
|
||||
<tr key={fund.id} className="transition-colors hover:bg-slate-50">
|
||||
<Td className="font-medium text-slate-900">{fund.product_code}</Td>
|
||||
<Td>{fund.product_name}</Td>
|
||||
<Td>{fund.product_type}</Td>
|
||||
<Td>{fund.risk_level}</Td>
|
||||
<Td className="text-right">{fund.nav?.toFixed(4) ?? "--"}</Td>
|
||||
<Td className="text-xs text-slate-500">{fund.nav_date ?? "--"}</Td>
|
||||
<Td>{fund.fund_manager ?? "--"}</Td>
|
||||
<Td>
|
||||
<Badge tone={statusTone(fund.status)}>{fund.status}</Badge>
|
||||
</Td>
|
||||
<Td>
|
||||
<Link
|
||||
href={`/products/${fund.product_code}`}
|
||||
className="block text-right text-sm text-indigo-600 hover:underline"
|
||||
>
|
||||
详情
|
||||
</Link>
|
||||
</Td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableWrap>
|
||||
<Pagination page={page} totalPages={totalPages} total={total} onChange={setPage} />
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user