Files
one_divine_lot/src/client/views/AllPositionsTab.jsx
T
kyugao 2790b92378 feat(策略持仓): 持仓行展开关联交易 + 成本价/最后成交价 + 隐藏输入框(迭代 08,R-010)
- strategy-positions 附加 holding_id + avgPrice/lastTradePrice(无关联默认成本价)
- trades/by-holding 端点(按 holding 查委托汇总)
- StrategyTab 持仓行展开(懒加载 + 内嵌汇总表,含交易日/时间列)
- 神之一手 tab 激活时隐藏 AI 对话输入框(纯 CSS :has() 方案)
2026-09-02 01:20:58 +08:00

76 lines
3.3 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 全部持仓 tab 组件
* 显示全量真实持仓(QMT),标注每只票的份额分配
*/
import { useState, useEffect, useCallback } from 'react';
import { useRpc } from './connection.jsx';
import { LoadState } from './LoadState.jsx';
import { useMarket } from '../market/MarketDataProvider.jsx';
import { PriceCell } from './PriceCell.jsx';
export function AllPositionsTab(props) {
const [positions, setPositions] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
const call = useRpc();
const { getPrice, registerCodes } = useMarket();
const load = useCallback(async () => {
setLoading(true);
setError(null);
try {
const res = await call('one-divine-lot/summary');
if (res && res.ok) {
setPositions(res.value);
// 上报持仓 code 给行情 Provider(去重:不再由 Provider 自拉 positions
const codes = (res.value?.positions ?? []).map((p) => p.code).filter(Boolean);
registerCodes(codes);
} else setError((res && res.error && res.error.message) || '加载失败');
} catch (e) {
setError(e.message);
} finally {
setLoading(false);
}
}, [call]);
useEffect(() => {
load();
}, [load]);
return (
<div className="odl-root" style={{ padding: 16, fontFamily: 'system-ui' }}>
<h3 style={{ margin: '0 0 12px' }}>全部持仓{positions ? positions.positions.length : 0} </h3>
<LoadState loading={loading} error={error} onRetry={load} autoRetry={2}>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
<thead>
<tr style={{ textAlign: 'left', borderBottom: '2px solid #ddd' }}>
<th style={{ padding: '8px 6px', lineHeight: '24px' }}>代码</th>
<th style={{ padding: '8px 6px', lineHeight: '24px' }}>名称</th>
<th style={{ padding: '8px 6px', lineHeight: '24px' }}>现价</th>
<th style={{ padding: '8px 6px', lineHeight: '24px' }}>总持仓</th>
<th style={{ padding: '8px 6px', lineHeight: '24px' }}>已分配</th>
<th style={{ padding: '8px 6px', lineHeight: '24px' }}>未分配</th>
<th style={{ padding: '8px 6px', lineHeight: '24px' }}>分配详情</th>
</tr>
</thead>
<tbody>
{(positions?.positions ?? []).map((p) => (
<tr key={p.code} style={{ borderBottom: '1px solid #eee' }}>
<td style={{ padding: '8px 6px', lineHeight: '24px' }}>{p.code}</td>
<td style={{ padding: '8px 6px', lineHeight: '24px' }}>{p.name}</td>
<PriceCell price={getPrice(p.code)?.lastPrice} lastClose={getPrice(p.code)?.lastClose} />
<td style={{ padding: '8px 6px', lineHeight: '24px' }}>{p.totalVolume}</td>
<td style={{ padding: '8px 6px', lineHeight: '24px' }}>{p.allocated}</td>
<td style={{ padding: '8px 6px', lineHeight: '24px', color: p.unallocated > 0 ? '#e65100' : '#2e7d32' }}>{p.unallocated}</td>
<td style={{ padding: '8px 6px', lineHeight: '24px', fontSize: 12, color: '#555' }}>
{Object.entries(p.shares).map(([sid, n]) => sid + ': ' + n).join(', ') || '—'}
</td>
</tr>
))}
</tbody>
</table>
</LoadState>
</div>
);
}