# Sanguo VeighNa Web 前端对等实现计划
## 目标
使 Web 前端功能与 VeighNa 4.4 原生 Qt UI 对等,提供完整的交易功能。
---
## 一、需求概览
基于 `requirements/veighna-ui-analysis.md` 的深度分析,需要实现以下功能:
### 1.1 核心缺失功能(Phase 1 - 高优先级)
- [x] 成交监控页面
- [x] 资金监控页面
- [x] 网关连接管理
### 1.2 增强功能(Phase 2 - 中优先级)
- [x] 活动委托视图
- [x] 市场深度盘口
- [x] 合约管理
- [x] 表格排序
### 1.3 完善功能(Phase 3 - 低优先级)
- [ ] 双击交互
- [ ] CSV 导出
- [ ] 全局配置编辑
- [ ] 微信通知设置
---
## 二、技术架构
### 2.1 现有架构
```
sanguo_web/
├── api/ # FastAPI 端点
├── services/ # 业务逻辑
├── websocket/ # WebSocket 处理
├── static/
│ ├── css/ # 样式
│ └── js/ # Vue.js 应用
└── templates/ # HTML 模板
```
### 2.2 后端扩展
#### API 端点
| 端点 | 方法 | 功能 |
|------|------|------|
| /api/trades | GET | 获取所有成交记录 |
| /api/accounts | GET | 获取所有账户资金 |
| /api/gateways | GET | 获取所有可用网关 |
| /api/gateways/{name}/setting | GET | 获取网关配置模板 |
| /api/gateways/{name}/connect | POST | 连接网关 |
| /api/gateways/{name}/disconnect | POST | 断开网关 |
| /api/gateways/{name}/status | GET | 获取网关状态 |
| /api/contracts | GET | 获取所有合约 |
#### WebSocket 事件
| 事件 | 说明 |
|------|------|
| EVENT_TRADE | 成交数据推送 |
| EVENT_ACCOUNT | 账户数据推送 |
| EVENT_GATEWAY | 网关状态变化 |
### 2.3 前端扩展
#### 新增页面组件
| 组件 | 路由 | 功能 |
|------|------|------|
| TradesPage | /trades | 成交监控表格 |
| AccountPage | /account | 资金监控表格 |
| GatewayPage | /gateway | 网关管理 |
| ContractPage | /contracts | 合约查询 |
| ActiveOrdersPage | /active-orders | 活动委托 |
#### 导航菜单扩展
```javascript
const navItems = [
{ id: 'dashboard', label: '总览', icon: '' },
{ id: 'market', label: '行情', icon: '' },
{ id: 'trading', label: '交易', icon: '' },
{ id: 'trades', label: '成交', icon: '' }, // 新增
{ id: 'position', label: '持仓', icon: '' },
{ id: 'account', label: '资金', icon: '' }, // 新增
{ id: 'gateway', label: '网关', icon: '' }, // 新增
{ id: 'contracts', label: '合约', icon: '' }, // 新增
{ id: 'strategy', label: '策略', icon: '' },
{ id: 'log', label: '日志', icon: '' }
];
```
---
## 三、Phase 1 实现方案
### 3.1 成交监控页面 (TradesPage)
#### 后端实现
**文件**: `sanguo_web/api/trades.py`
```python
from fastapi import APIRouter, Depends
from typing import List
from vnpy.trader.object import TradeData
router = APIRouter(prefix="/api/trades", tags=["trades"])
@router.get("/", response_model=List[dict])
async def get_all_trades():
"""获取所有成交记录"""
from sanguo_web.services.main_service import main_engine
trades = main_engine.get_all_trades()
return [t.__dict__ for t in trades]
```
#### WebSocket 事件
**文件**: `sanguo_web/websocket/events.py`
```python
def handle_trade_event(event: Event):
"""处理成交事件"""
trade = event.data
# 推送到前端
broadcast_event("trade", {
"tradeid": trade.tradeid,
"orderid": trade.orderid,
"symbol": trade.symbol,
"exchange": trade.exchange.value,
"direction": trade.direction.value,
"offset": trade.offset.value,
"price": trade.price,
"volume": trade.volume,
"datetime": trade.datetime.strftime("%H:%M:%S"),
"gateway_name": trade.gateway_name
})
```
#### 前端实现
**文件**: `sanguo_web/static/js/app.js`
```javascript
// 成交数据
const trades = ref([]);
// 获取成交数据
async function loadTrades() {
const response = await fetch('/api/trades');
trades.value = await response.json();
}
// WebSocket 处理
ws.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.type === 'trade') {
trades.value.unshift(data.payload);
}
});
```
#### HTML 模板
```html
| 成交号 |
委托号 |
合约 |
方向 |
价格 |
数量 |
时间 |
| {{ trade.tradeid }} |
{{ trade.orderid }} |
{{ trade.symbol }} |
{{ trade.direction }} |
{{ formatNumber(trade.price) }} |
{{ trade.volume }} |
{{ trade.datetime }} |
```
---
### 3.2 资金监控页面 (AccountPage)
#### 后端实现
**文件**: `sanguo_web/api/accounts.py`
```python
from fastapi import APIRouter
from typing import List
router = APIRouter(prefix="/api/accounts", tags=["accounts"])
@router.get("/", response_model=List[dict])
async def get_all_accounts():
"""获取所有账户资金"""
from sanguo_web.services.main_service import main_engine
accounts = main_engine.get_all_accounts()
return [a.__dict__ for a in accounts]
```
#### WebSocket 事件
```python
def handle_account_event(event: Event):
"""处理账户事件"""
account = event.data
broadcast_event("account", {
"accountid": account.accountid,
"balance": account.balance,
"frozen": account.frozen,
"available": account.available,
"gateway_name": account.gateway_name
})
```
#### 前端实现
```javascript
// 账户数据
const accounts = ref([]);
// 获取账户数据
async function loadAccounts() {
const response = await fetch('/api/accounts');
accounts.value = await response.json();
}
// WebSocket 处理
ws.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.type === 'account') {
const index = accounts.value.findIndex(a => a.accountid === data.payload.accountid);
if (index >= 0) {
accounts.value[index] = data.payload;
} else {
accounts.value.push(data.payload);
}
}
});
```
---
### 3.3 网关连接管理
#### 后端实现
**文件**: `sanguo_web/api/gateways.py`
```python
from fastapi import APIRouter
from typing import List, Dict, Any
router = APIRouter(prefix="/api/gateways", tags=["gateways"])
@router.get("/", response_model=List[str])
async def get_all_gateways():
"""获取所有可用网关"""
from sanguo_web.services.main_service import main_engine
return main_engine.get_all_gateway_names()
@router.get("/{name}/setting")
async def get_gateway_setting(name: str):
"""获取网关配置模板"""
from sanguo_web.services.main_service import main_engine
return main_engine.get_default_setting(name)
@router.post("/{name}/connect")
async def connect_gateway(name: str, setting: Dict[str, Any]):
"""连接网关"""
from sanguo_web.services.main_service import main_engine
main_engine.connect(setting, name)
return {"status": "connecting"}
@router.post("/{name}/disconnect")
async def disconnect_gateway(name: str):
"""断开网关"""
from sanguo_web.services.main_service import main_engine
main_engine.disconnect(name)
return {"status": "disconnected"}
@router.get("/{name}/status")
async def get_gateway_status(name: str):
"""获取网关状态"""
from sanguo_web.services.main_service import main_engine
# 需要扩展 MainEngine 支持状态查询
return {"name": name, "status": "connected"}
```
#### 前端实现
```javascript
// 网关数据
const gateways = ref([]);
const selectedGateway = ref(null);
const gatewayForm = ref({});
const showConnectDialog = ref(false);
// 获取网关列表
async function loadGateways() {
const response = await fetch('/api/gateways');
gateways.value = await response.json();
}
// 获取网关配置模板
async function openConnectDialog(gatewayName) {
const response = await fetch(`/api/gateways/${gatewayName}/setting`);
const setting = await response.json();
gatewayForm.value = setting;
selectedGateway.value = gatewayName;
showConnectDialog.value = true;
}
// 连接网关
async function connectGateway() {
const response = await fetch(`/api/gateways/${selectedGateway.value}/connect`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(gatewayForm.value)
});
showConnectDialog.value = false;
// 刷新网关状态
}
```
#### HTML 模板(连接对话框)
```html
```
---
## 四、实现顺序
### Step 1: 后端基础设施(1-2天)
1. 创建 API 路由文件
- `sanguo_web/api/trades.py`
- `sanguo_web/api/accounts.py`
- `sanguo_web/api/gateways.py`
2. 扩展 WebSocket 事件处理
- 添加 EVENT_TRADE 处理
- 添加 EVENT_ACCOUNT 处理
3. 注册新路由到主应用
### Step 2: 前端页面开发(2-3天)
1. 扩展导航菜单
2. 实现成交监控页面
3. 实现资金监控页面
4. 实现网关管理页面
5. 添加相关样式
### Step 3: 集成测试(1天)
1. API 接口测试
2. WebSocket 事件测试
3. 端到端功能测试
4. 修复发现的问题
### Step 4: 代码审查(1天)
1. 前端代码审查
2. 后端代码审查
3. 安全性检查
### Step 5: 部署和验证(0.5天)
1. 更新文档
2. 部署验证
3. 性能测试
---
## 五、关键实现细节
### 5.1 网关动态表单
网关配置模板由各网关心提供,格式为:
```python
{
"字段名": "默认值", # 类型可以是 str, int, bool, list
...
}
```
前端需要根据类型动态渲染:
- `str` → 文本输入框
- `int` → 数字输入框
- `bool` → 复选框
- `list` → 下拉选择框
### 5.2 实时数据推送
使用 WebSocket 推送实时数据:
- 新数据插入到数组头部
- 对于有唯一键的数据(如账户),更新现有条目
- 对于无唯一键的数据(如成交),只插入新条目
### 5.3 状态管理
使用 Vue 3 的 reactive/ref 管理状态:
- 每个页面有独立的数据 ref
- WebSocket 统一处理,根据事件类型更新对应数据
- 避免全局状态污染
---
## 六、验收标准
### 6.1 功能验收
- [ ] 成交监控页面能正确显示所有成交记录
- [ ] 成交记录能实时更新
- [ ] 资金监控页面能正确显示所有账户
- [ ] 账户资金能实时更新
- [ ] 网关列表能正确显示所有可用网关
- [ ] 能成功连接/断开网关
### 6.2 性能验收
- [ ] 页面加载时间 < 2秒
- [ ] WebSocket 延迟 < 100ms
- [ ] 表格渲染 100 条数据 < 500ms
### 6.3 兼容性验收
- [ ] Chrome 浏览器正常
- [ ] Firefox 浏览器正常
- [ ] Safari 浏览器正常
---
## 七、后续阶段预览
### Phase 2
- 活动委托视图
- 市场深度盘口(五档)
- 合约管理页面
- 表格排序功能
### Phase 3
- 双击交互(撤单、更新交易表单)
- CSV 导出
- 全局配置编辑器
- 微信通知设置
---
## 六、实施进度
### Phase 1 后端 (已完成 2026-07-04)
| 步骤 | 状态 | 说明 |
|------|------|------|
| 创建 API 路由 | ✅ | trades.py, accounts.py, settings.py |
| 注册路由 | ✅ | __init__.py 已更新 |
| 修复模块加载 | ✅ | run_web.py 添加 vnpy 路径 |
| 修复循环导入 | ✅ | deps.py 使用动态导入 |
| 添加服务字段 | ✅ | main_service.py 添加 offset, gateway_name |
| 生产部署 | ✅ | 已部署到 ~/.sanguo_projects/sanguo_vnpy_v2 |
| 文档更新 | ✅ | docs/deployment/README.md 已更新 |
### 待完成
| 步骤 | 状态 | 说明 |
|------|------|------|
| 前端页面开发 | ⏳ | 成交监控、资金监控、网关管理页面 |
| WebSocket 事件 | ⏳ | EVENT_TRADE, EVENT_ACCOUNT 处理 |
| 集成测试 | ⏳ | API 和 WebSocket 测试 |
| 代码审查 | ⏳ | 前后端代码审查 |
---
*计划制定日期: 2026-07-02*
*预计完成时间: Phase 1 约 5-6 天*
*Phase 1 后端完成日期: 2026-07-04*