统一单入口 Go 工程与前端依赖目录,增加后台供应商配置、候选审核、计划预算、成交冲正、持仓复盘和站内通知。接入策略版本、Python 回测、可配置 A 股公开行情及 zap 文件轮转日志,补充迁移、契约与验证。
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import {useState} from "react"
|
||||
import {useQueryClient} from "@tanstack/react-query"
|
||||
import {Text,View} from "react-native"
|
||||
import {useAccounts,useSwitchAccount} from "../../src/state/accounts"
|
||||
import {useSession} from "../../src/state/session"
|
||||
import {useAccountSelection} from "../../src/state/account"
|
||||
import {formatMoney,validInitialCash} from "../../src/lib/money"
|
||||
import type {Account} from "../../src/lib/types"
|
||||
import {Body,Button,ErrorMessage,Field,Panel,Screen,Title,styles} from "../../src/components/ui"
|
||||
export default function Accounts(){
|
||||
const {client,session}=useSession();const {accounts,selected,isPending,isError,error:queryError,refetch}=useAccounts();const switchAccount=useSwitchAccount();const queryClient=useQueryClient()
|
||||
const [mode,setMode]=useState<Account["mode"]>("simulation");const [name,setName]=useState("");const [cash,setCash]=useState("");const [error,setError]=useState("");const [notice,setNotice]=useState("");const [busy,setBusy]=useState(false)
|
||||
const alreadyExists=accounts.some(account=>account.mode===mode)
|
||||
async function submit(){if(busy||alreadyExists)return;setBusy(true);setError("");setNotice("");try{
|
||||
if(!name.trim())throw new Error("请填写账户名称。")
|
||||
if(mode==="simulation"&&!validInitialCash(cash))throw new Error("初始资金应大于 0,不超过 10 亿元,最多两位小数。")
|
||||
const account=await client<Account>("/api/v1/accounts",{method:"POST",body:{mode,name:name.trim(),...(mode==="simulation"?{initial_cash:cash}:{})}})
|
||||
useAccountSelection.getState().select(account.id);setName("");setCash("");setNotice("账户已创建,初始资金以服务端账本为准。");await queryClient.invalidateQueries({queryKey:["accounts",session?.user.id]})
|
||||
}catch(e){setError(e instanceof Error?e.message:"创建账户失败,请重试。");void refetch()}finally{setBusy(false)}}
|
||||
async function select(id:string){setError("");try{await switchAccount(id);setNotice("已切换账户。")}catch{setError("账户切换失败,请重试。")}}
|
||||
return <Screen><Title>账户管理</Title><Body>模拟账户和真实记录账户独立保存,每种模式各可创建一个。</Body>{isPending?<Body>正在读取账户…</Body>:isError?<Panel><ErrorMessage message={queryError.message}/><Button title="重试" secondary onPress={()=>void refetch()}/></Panel>:accounts.map(account=><Panel key={account.id}><View style={styles.row}><Text style={styles.subtitle}>{account.name}</Text><Text style={styles.badge}>{account.mode==="simulation"?"模拟":"手工真实记录"}</Text></View><Body>账本现金:{formatMoney(account.cash_balance)}{account.cash_balance!==null?" 元":""}</Body><Button title={selected?.id===account.id?"当前账户":"切换到此账户"} secondary disabled={selected?.id===account.id||busy} onPress={()=>void select(account.id)}/></Panel>)}{notice&&<Text accessibilityRole="alert" style={{color:"#6554A5"}}>{notice}</Text>}<ErrorMessage message={error}/><View style={styles.divider}/><Text style={styles.subtitle}>创建账户</Text><View style={styles.row}><Button title="模拟账户" secondary={mode!=="simulation"} disabled={busy} onPress={()=>{setMode("simulation");setError("")}}/><Button title="真实记录" secondary={mode!=="manual_real"} disabled={busy} onPress={()=>{setMode("manual_real");setError("")}}/></View>{alreadyExists?<Body>该模式的账户已存在,可在上方切换使用。</Body>:<><Field label="账户名称" value={name} onChangeText={setName} maxLength={80} editable={!busy}/>{mode==="simulation"?<Field label="初始模拟资金(元)" value={cash} onChangeText={setCash} keyboardType="decimal-pad" maxLength={13} editable={!busy}/>:<Panel accent><Body>真实记录由你手工维护,未经券商核验。创建时不注入模拟资金,现金余额为未知。</Body></Panel>}<Button title={mode==="simulation"?"确认创建模拟账户":"确认创建真实记录账户"} busy={busy} disabled={isPending||isError||!name.trim()||(mode==="simulation"&&!cash)} onPress={()=>void submit()}/></>}</Screen>
|
||||
}
|
||||
Reference in New Issue
Block a user