diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md deleted file mode 100644 index 643577d..0000000 --- a/frontend/AGENTS.md +++ /dev/null @@ -1,9 +0,0 @@ - - -# This is NOT the Next.js you know - -This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. - -This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. - - diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md deleted file mode 100644 index 43c994c..0000000 --- a/frontend/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -@AGENTS.md diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index 7d8d1ee..0000000 --- a/frontend/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# 华夏基金 PC 前端 - -独立的用户端官网与员工后台前端工程。 - -## 当前状态 - -已建立 Next.js + React + TypeScript + Tailwind CSS + Lucide 的基础工程和页面路由占位。 - -## 启动 - -```bash -npm install -npm run dev -``` - -默认访问地址:`http://localhost:3000` - -## 配置 - -复制 `.env.example` 为 `.env.local`,根据实际后端地址调整: - -```env -NEXT_PUBLIC_API_BASE_URL=/backend -BACKEND_API_ORIGIN=http://127.0.0.1:8000 - -前端默认通过 Next.js 同源代理访问后端,避免浏览器 CORS 预检失败。 -``` - -## 边界 - -- 只开发 PC 前端。 -- 不修改项目根目录下现有 FastAPI 后端。 -- 基金数据通过后端 API 获取,不直接连接 MySQL。 -- 角色显示由前端映射为“投顾专员”“风控专员”“员工通用”。 diff --git a/frontend/app/account/adjust/page.tsx b/frontend/app/account/adjust/page.tsx deleted file mode 100644 index 969946e..0000000 --- a/frontend/app/account/adjust/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -"use client"; - -import Link from "next/link"; -import { ArrowLeft } from "lucide-react"; -import { FormEvent, useState } from "react"; -import { apiFetch } from "@/lib/api"; - -export default function AccountAdjustPage() { - const [direction, setDirection] = useState<"add" | "sub">("add"); const [amount, setAmount] = useState(""); const [message, setMessage] = useState(""); - async function submit(event: FormEvent) { event.preventDefault(); try { await apiFetch("/account/adjust", { method: "POST", body: JSON.stringify({ direction, amount }) }); setMessage("余额调整已提交"); setAmount(""); } catch (error) { setMessage(error instanceof Error ? error.message : "余额调整失败"); } } - return
返回用户中心

账户余额调整

充值或提现将由后端进行权限与金额校验。

{message &&

{message}

}
; -} diff --git a/frontend/app/account/page.tsx b/frontend/app/account/page.tsx deleted file mode 100644 index 1b7e6c5..0000000 --- a/frontend/app/account/page.tsx +++ /dev/null @@ -1,23 +0,0 @@ -"use client"; - -import Link from "next/link"; -import { ArrowLeft, ArrowUpRight, CircleUserRound, Landmark, RefreshCw, ShieldCheck, WalletCards, X } from "lucide-react"; -import { FormEvent, useEffect, useState } from "react"; -import { apiFetch } from "@/lib/api"; -import { clearToken } from "@/lib/auth"; -import type { UserPayload } from "@/types/api"; - -interface Balance { balance?: number | string; available_balance?: number | string; } -interface Holding { product_id?: number; product_name?: string; product_code?: string; shares?: number | string; current_value?: number | string; market_value?: number | string; profit_loss?: number | string; } - -export default function AccountPage() { - const [user, setUser] = useState(null); const [balance, setBalance] = useState(null); const [holdings, setHoldings] = useState([]); const [state, setState] = useState<"loading" | "ready" | "error">("loading"); const [action, setAction] = useState<"purchase" | "redeem" | null>(null); - async function loadAccount() { setState("loading"); try { const [me, account, holdingList] = await Promise.all([apiFetch<{ user: UserPayload }>("/auth/me"), apiFetch("/account/balance"), apiFetch("/holdings")]); setUser(me.user); setBalance(account); setHoldings(holdingList); setState("ready"); } catch { setState("error"); } } - useEffect(() => { void loadAccount(); }, []); - function signOut() { clearToken(); window.location.href = "/login"; } - return
华华夏基金
基金产品
返回首页

Personal Account

用户中心

{user?.real_name ?? user?.username ?? "您的华夏基金账户"}

{state === "loading" && }{state === "error" && }{state === "ready" && <>

账户余额

¥{balance?.balance ?? "--"}

可用余额

¥{balance?.available_balance ?? "--"}

风险测评

待完成测评

当前后端仅提供提交接口

我的持仓

{holdings.length ?
{holdings.map((item) =>

{item.product_name ?? "基金产品"}

{item.product_code}

¥{item.current_value ?? item.market_value ?? "--"}

份额 {item.shares ?? "--"} · 盈亏 {item.profit_loss ?? "--"}

)}
:

暂无持仓数据

}

快捷操作

交易记录

当前后端暂未提供交易记录查询接口,页面不会展示虚构数据。

消息通知

当前后端暂未提供用户消息查询接口。

}
{action && setAction(null)} onDone={() => { setAction(null); void loadAccount(); }} />}
; -} - -function TradeModal({ type, holdings, onClose, onDone }: { type: "purchase" | "redeem"; holdings: Holding[]; onClose: () => void; onDone: () => void }) { const [productId, setProductId] = useState(String(holdings[0]?.product_id ?? "")); const [value, setValue] = useState(""); const [message, setMessage] = useState(""); const [loading, setLoading] = useState(false); async function submit(event: FormEvent) { event.preventDefault(); setLoading(true); setMessage(""); try { await apiFetch(type === "purchase" ? "/purchase" : "/redeem", { method: "POST", body: JSON.stringify(type === "purchase" ? { product_id: Number(productId), amount: value } : { product_id: Number(productId), shares: value }) }); onDone(); } catch (error) { setMessage(error instanceof Error ? error.message : "操作失败,请稍后重试"); } finally { setLoading(false); } } return

{type === "purchase" ? "申购基金" : "赎回基金"}

{message &&

{message}

}
; } - -function Notice({ text }: { text: string }) { return
{text}
; } diff --git a/frontend/app/admin/advisor/page.tsx b/frontend/app/admin/advisor/page.tsx deleted file mode 100644 index 200eb60..0000000 --- a/frontend/app/admin/advisor/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { AdvisorWorkspace } from "@/components/role-workspaces"; -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" />