From ff37553e62d6db33450dd6303e70f40ab398b7ba Mon Sep 17 00:00:00 2001 From: lpm Date: Mon, 14 Sep 2026 19:00:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=89=8D=E7=AB=AF=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + frontend/app/admin/administrator/page.tsx | 5 + frontend/app/admin/advisor/page.tsx | 7 +- frontend/app/admin/advisor/tools/page.tsx | 26 - frontend/app/admin/common/page.tsx | 7 +- frontend/app/admin/risk/page.tsx | 7 +- frontend/app/page.tsx | 3 +- frontend/app/products/page.tsx | 77 ++- frontend/app/tools/knowledge/page.tsx | 6 +- frontend/app/tools/nl2sql/page.tsx | 7 +- frontend/components/admin/sections/admin.tsx | 70 +++ .../components/admin/sections/customers.tsx | 466 +++++++++++++++++ frontend/components/admin/sections/drafts.tsx | 199 ++++++++ frontend/components/admin/sections/funds.tsx | 152 ++++++ .../components/admin/sections/overview.tsx | 318 ++++++++++++ frontend/components/admin/sections/risk.tsx | 278 ++++++++++ frontend/components/admin/sections/todos.tsx | 179 +++++++ frontend/components/admin/sections/visits.tsx | 229 +++++++++ .../components/admin/sections/workorders.tsx | 282 +++++++++++ frontend/components/admin/shell.tsx | 274 ++++++++++ frontend/components/admin/ui.tsx | 479 ++++++++++++++++++ .../components/admin/unified-workspace.tsx | 212 ++++++++ frontend/components/agent-mount.tsx | 12 +- frontend/components/role-workspaces.tsx | 46 -- frontend/lib/admin-api.ts | 266 ++++++++++ frontend/lib/auth.ts | 43 +- frontend/lib/risk.ts | 18 + frontend/lib/roles.ts | 90 ++++ frontend/next-env.d.ts | 4 +- sql/schema.sql | 4 +- 30 files changed, 3635 insertions(+), 136 deletions(-) create mode 100644 frontend/app/admin/administrator/page.tsx delete mode 100644 frontend/app/admin/advisor/tools/page.tsx create mode 100644 frontend/components/admin/sections/admin.tsx create mode 100644 frontend/components/admin/sections/customers.tsx create mode 100644 frontend/components/admin/sections/drafts.tsx create mode 100644 frontend/components/admin/sections/funds.tsx create mode 100644 frontend/components/admin/sections/overview.tsx create mode 100644 frontend/components/admin/sections/risk.tsx create mode 100644 frontend/components/admin/sections/todos.tsx create mode 100644 frontend/components/admin/sections/visits.tsx create mode 100644 frontend/components/admin/sections/workorders.tsx create mode 100644 frontend/components/admin/shell.tsx create mode 100644 frontend/components/admin/ui.tsx create mode 100644 frontend/components/admin/unified-workspace.tsx delete mode 100644 frontend/components/role-workspaces.tsx create mode 100644 frontend/lib/admin-api.ts create mode 100644 frontend/lib/risk.ts create mode 100644 frontend/lib/roles.ts diff --git a/.gitignore b/.gitignore index 5f822aa..67aeed5 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,8 @@ DK2_客服Agent模块完整开发计划(v1.1).md DK4_客服Agent需求文档(修复完善版v1.4).md 客服Agent模块 · TODO List(最终修复版v1.2).md 客服Agent长期记忆与置信度最终开发文档.md +开发计划.md +开发计划-前端.md +开发计划_用户画像置信度实时更新.md +需求文档.md +风控Agent需求文档.md \ No newline at end of file diff --git a/frontend/app/admin/administrator/page.tsx b/frontend/app/admin/administrator/page.tsx new file mode 100644 index 0000000..7030cb9 --- /dev/null +++ b/frontend/app/admin/administrator/page.tsx @@ -0,0 +1,5 @@ +import { UnifiedWorkspace } from "@/components/admin/unified-workspace"; + +export default function AdministratorPage() { + return ; +} diff --git a/frontend/app/admin/advisor/page.tsx b/frontend/app/admin/advisor/page.tsx index 200eb60..414dd09 100644 --- a/frontend/app/admin/advisor/page.tsx +++ b/frontend/app/admin/advisor/page.tsx @@ -1,2 +1,5 @@ -import { AdvisorWorkspace } from "@/components/role-workspaces"; -export default function AdvisorPage() { return ; } +import { UnifiedWorkspace } from "@/components/admin/unified-workspace"; + +export default function AdvisorPage() { + return ; +} diff --git a/frontend/app/admin/advisor/tools/page.tsx b/frontend/app/admin/advisor/tools/page.tsx deleted file mode 100644 index 5a58f8f..0000000 --- a/frontend/app/admin/advisor/tools/page.tsx +++ /dev/null @@ -1,26 +0,0 @@ -"use client"; - -import Link from "next/link"; -import { ArrowLeft, Download, RefreshCw, Save, Send, Trash2 } from "lucide-react"; -import { FormEvent, useEffect, useState } from "react"; -import { apiFetch } from "@/lib/api"; - -interface Customer { customer_id?: number; id?: number; real_name?: string; name?: string; } -interface Draft { draft_id?: string; id?: string; title?: string; content?: string; status?: string; customer_id?: number; } -interface Visit { id: number; customer_id: number; visit_type?: string; visit_time?: string; summary?: string; content?: string; } - -export default function AdvisorToolsPage() { - const [customers, setCustomers] = useState([]); const [customerId, setCustomerId] = useState(""); const [section, setSection] = useState("detail"); const [data, setData] = useState(null); const [message, setMessage] = useState(""); - const [drafts, setDrafts] = useState([]); const [draftId, setDraftId] = useState(""); const [draftTitle, setDraftTitle] = useState(""); const [draftContent, setDraftContent] = useState(""); - const [visits, setVisits] = useState([]); const [visitId, setVisitId] = useState(null); const [visitType, setVisitType] = useState("电话回访"); const [visitTime, setVisitTime] = useState(""); const [visitSummary, setVisitSummary] = useState(""); - useEffect(() => { void apiFetch<{ items?: Customer[] }>("/advisor/customers?page=1&page_size=100").then((result) => { setCustomers(result.items ?? []); const first = result.items?.[0]; if (first) setCustomerId(String(first.customer_id ?? first.id)); }).catch(() => setMessage("客户列表加载失败")); }, []); - async function load() { setMessage(""); try { const id = Number(customerId); const paths: Record = { detail: `/advisor/customers/${id}`, holdings: `/advisor/customers/${id}/holdings`, reports: `/advisor/customers/${id}/reports`, diagnosis: `/advisor/diagnosis/${id}`, strategies: "/advisor/strategies", funds: "/advisor/funds", audit: "/advisor/audit/ledger" }; if (section === "drafts") { const result = await apiFetch<{ items?: Draft[] }>(`/advisor/drafts?page=1&page_size=50${customerId ? `&customer_id=${id}` : ""}`); setDrafts(result.items ?? []); setData(result); return; } if (section === "visits") { const result = await apiFetch<{ items?: Visit[] }>(`/advisor/visits?page=1&page_size=50${customerId ? `&customer_id=${id}` : ""}`); setVisits(result.items ?? []); setData(result); return; } setData(await apiFetch(paths[section])); } catch (error) { setMessage(error instanceof Error ? error.message : "加载失败"); } } - function chooseDraft(draft: Draft) { const id = String(draft.draft_id ?? draft.id ?? ""); setDraftId(id); setDraftTitle(draft.title ?? ""); setDraftContent(draft.content ?? ""); } - async function draftAction(action: "save" | "discard" | "send") { if (!draftId) return; try { if (action === "save") await apiFetch(`/advisor/drafts/${draftId}/save`, { method: "PUT", body: JSON.stringify({ title: draftTitle, content: draftContent }) }); else await apiFetch(`/advisor/drafts/${draftId}/${action}`, { method: "POST" }); setMessage(action === "save" ? "草稿已保存" : action === "discard" ? "草稿已废弃" : "草稿已发送"); await load(); } catch (error) { setMessage(error instanceof Error ? error.message : "草稿操作失败"); } } - async function saveVisit(event: FormEvent) { event.preventDefault(); try { const body = { customer_id: Number(customerId), visit_type: visitType, visit_time: new Date(visitTime).toISOString(), summary: visitSummary }; if (visitId) await apiFetch(`/advisor/visits/${visitId}`, { method: "PUT", body: JSON.stringify(body) }); else await apiFetch("/advisor/visits", { method: "POST", body: JSON.stringify(body) }); setMessage(visitId ? "回访已更新" : "回访已新增"); setVisitId(null); setVisitSummary(""); await load(); } catch (error) { setMessage(error instanceof Error ? error.message : "回访保存失败"); } } - return
返回投顾工作区

投顾业务工具

{section === "audit" && }
{message &&

{message}

} - {section === "drafts" &&

草稿列表

{drafts.map((draft) => )}{!drafts.length &&

暂无草稿

}
setDraftTitle(event.target.value)} placeholder="草稿标题" className="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm" />