# 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*