使用go-via采用ag-grid模拟显示股票信息

package main

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/go-via/via"
	"github.com/go-via/via/h"
)

// 股票数据结构
type Stock struct {
	Code          string  `json:"code"`
	Name          string  `json:"name"`
	Price         float64 `json:"price"`
	Change        float64 `json:"change"`
	ChangePercent float64 `json:"changePercent"`
}

// 股票数据包含状态和时间戳
type StockData struct {
	Stocks   []Stock `json:"stocks"`
	UpdateAt string  `json:"updateAt"`
}

// toJSON 将数据转换为JSON字符串
func toJSON(v interface{}) string {
	data, err := json.Marshal(v)
	if err != nil {
		return "{}"
	}
	return string(data)
}

func main() {
	app := via.New(
		via.WithTitle("股票实时行情"),
		via.WithAddr(":8080"),
	)

	// 初始股票数据
	initialStocks := []Stock{
		{Code: "600000", Name: "浦发银行", Price: 8.62, Change: 0, ChangePercent: 0},
		{Code: "600036", Name: "招商银行", Price: 35.45, Change: 0, ChangePercent: 0},
		{Code: "000858", Name: "五粮液", Price: 178.20, Change: 0, ChangePercent: 0},
		{Code: "002594", Name: "比亚迪", Price: 235.80, Change: 0, ChangePercent: 0},
		{Code: "601318", Name: "中国平安", Price: 42.65, Change: 0, ChangePercent: 0},
		{Code: "600519", Name: "贵州茅台", Price: 1890.50, Change: 0, ChangePercent: 0},
		{Code: "300750", Name: "宁德时代", Price: 185.60, Change: 0, ChangePercent: 0},
		{Code: "601689", Name: "拓普集团", Price: 56.80, Change: 0, ChangePercent: 0},
	}

	// 添加AG-Grid库到head
	app.AppendToHead(h.Script(h.Src("https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js")))

	// 将主要JavaScript逻辑添加到head,完全用JavaScript更新数据
	app.AppendToHead(h.Script(h.Raw(`
		// 全局AG-Grid管理器 - 完全JavaScript更新
		window.AGGridManager = (function() {
			let gridApi = null;
			let gridInitialized = false;
			let dataUpdateInterval = null;
			let initializationPromise = null;
			let currentStocksData = [];

			// AG-Grid配置
			const gridOptions = {
				columnDefs: [
					{headerName: '代码', field: 'code', width: 100, pinned: 'left'},
					{headerName: '名称', field: 'name', width: 120, pinned: 'left'},
					{
						headerName: '价格',
						field: 'price',
						width: 100,
						type: 'numericColumn',
						valueFormatter: params => params.value.toFixed(2),
						cellStyle: {textAlign: 'right'},
						cellClass: params => params.data.change > 0 ? 'up' : (params.data.change < 0 ? 'down' : 'neutral')
					},
					{
						headerName: '涨跌',
						field: 'change',
						width: 100,
						type: 'numericColumn',
						valueFormatter: params => (params.value >= 0 ? '+' : '') + params.value.toFixed(2),
						cellStyle: {textAlign: 'right'},
						cellClass: params => params.value > 0 ? 'up' : (params.value < 0 ? 'down' : 'neutral')
					},
					{
						headerName: '涨跌幅',
						field: 'changePercent',
						width: 100,
						type: 'numericColumn',
						valueFormatter: params => (params.value >= 0 ? '+' : '') + params.value.toFixed(2) + '%',
						cellStyle: {textAlign: 'right'},
						cellClass: params => params.value > 0 ? 'up' : (params.value < 0 ? 'down' : 'neutral')
					}
				],
				rowData: [],
				defaultColDef: {sortable: true, filter: true, resizable: true, flex:1},
				rowHeight: 40,
				headerHeight: 50,
				animateRows: true,
				onGridReady: function(params) {
					gridApi = params.api;
					gridInitialized = true;
					loadInitialData();
					startDataUpdates();
				}
			};

			// 初始化AG-Grid
			function initialize() {
				if (initializationPromise) {
					return initializationPromise;
				}

				initializationPromise = new Promise(function(resolve, reject) {
					const gridContainer = document.getElementById('grid-container');

					if (!gridContainer) {
						setTimeout(function() {
							initialize().then(resolve).catch(reject);
						}, 100);
						return;
					}

					try {
						// agGrid.createGrid 直接返回 GridAPI,不是 Promise
						gridApi = agGrid.createGrid(gridContainer, gridOptions);
						gridInitialized = true;
						resolve();
					} catch (error) {
						reject(error);
					}
				});

				return initializationPromise;
			}

			// 更新股票数据(完全在前端)
			function updateStockPrices(stocks) {
				return stocks.map(function(stock) {
					const oldPrice = stock.price;
					const fluctuation = (Math.random() - 0.5) * 0.01;
					const newPrice = oldPrice * (1 + fluctuation);
					const change = newPrice - oldPrice;
					const changePercent = (change / oldPrice) * 100;
					
					return {
						...stock,
						price: newPrice,
						change: change,
						changePercent: changePercent
					};
				});
			}

			// 加载初始数据
			function loadInitialData() {
				const gridContainer = document.getElementById('grid-container');
				if (!gridContainer) {
					return;
				}

				const stocksData = gridContainer.getAttribute('data-stocks');
				const updateTime = gridContainer.getAttribute('data-updateAt');

				if (stocksData && updateTime) {
					try {
						const stocks = JSON.parse(stocksData);
						currentStocksData = stocks;

						if (gridApi && stocks && stocks.length > 0) {
							gridApi.setGridOption('rowData', stocks);
							const updateTimeElement = document.getElementById('updateTime');
							if (updateTimeElement) {
								updateTimeElement.textContent = updateTime;
							}
						}
					} catch (error) {
					}
				}
			}

			// 开始数据更新
			function startDataUpdates() {
				// 每2秒更新一次数据
				dataUpdateInterval = setInterval(function() {
					updateStockData();
				}, 2000);
			}

			// 更新股票数据
			function updateStockData() {
				if (!gridApi) {
					return;
				}

				try {
					// 更新股票价格
					const updatedStocks = updateStockPrices(currentStocksData);
					const updateTime = new Date().toLocaleTimeString('zh-CN', {hour12: false});

					// 更新grid
					gridApi.setGridOption('rowData', updatedStocks);
					
					// 更新显示时间
					const updateTimeElement = document.getElementById('updateTime');
					if (updateTimeElement) {
						updateTimeElement.textContent = updateTime;
					}

					// 保存更新后的数据
					currentStocksData = updatedStocks;
				} catch (error) {
				}
			}

			// 启动函数
			function start() {
				if (document.readyState === 'complete' || document.readyState === 'interactive') {
					setTimeout(function() {
						initialize().then(function() {
						}).catch(function(error) {
						});
					}, 100);
				} else {
					document.addEventListener('DOMContentLoaded', function() {
						setTimeout(function() {
							initialize().then(function() {
							}).catch(function(error) {
							});
						}, 100);
					});
				}
			}

			// 清理函数
			function cleanup() {
				if (dataUpdateInterval) {
					clearInterval(dataUpdateInterval);
					dataUpdateInterval = null;
				}
			}

			// 返回公共API
			return {
				start: start,
				cleanup: cleanup
			};
		})();

		// 页面加载完成后启动
		window.AGGridManager.start();

		// 窗口关闭时清理
		window.addEventListener('beforeunload', function() {
			window.AGGridManager.cleanup();
		});
	`)))

	// 添加自定义样式到head
	app.AppendToHead(h.StyleEl(
		h.Raw(`
			body {
				font-family: Arial, sans-serif;
				margin: 0;
				padding: 20px;
				background-color: #f5f5f5;
			}
			.container {
				max-width: 1200px;
				margin: 0 auto;
				background-color: white;
				padding: 20px;
				border-radius: 8px;
				box-shadow: 0 2px 4px rgba(0,0,0,0.1);
			}
			h1 {
				color: #333;
				margin-bottom: 10px;
			}
			.subtitle {
				color: #666;
				margin-bottom: 20px;
				font-size: 14px;
			}
			#grid-container {
				height: 500px;
				width: 100%;
			}
			.ag-theme-alpine {
				--ag-header-height: 50px;
				--ag-header-foreground-color: #333;
				--ag-header-background-color: #f0f0f0;
				--ag-odd-row-background-color: #f9f9f9;
			}
			.update-time {
				margin-top: 10px;
				font-size: 12px;
				color: #999;
			}
			.up { color: #ff4d4f; }
			.down { color: #52c41a; }
			.neutral { color: #999; }
		`),
	))

	// 主页路由 - 完全使用JavaScript更新,避免重新渲染
	app.Page("/", func(cmp *via.Cmp) {
		// 渲染静态视图 - 不使用via.State,避免重新渲染
		cmp.View(func(ctx *via.Ctx) h.H {
			return h.Div(
				h.Class("page-container"),
				h.Div(
					h.Class("container"),
					h.H1(h.Text("股票实时行情")),
					h.P(
						h.Class("subtitle"),
						h.Text("数据每2秒自动更新 | AG-Grid"),
					),
					// AG-Grid容器 - 初始数据在data属性中
					h.Div(
						h.ID("grid-container"),
						h.Class("ag-theme-alpine"),
						h.Data("stocks", toJSON(initialStocks)),
						h.Data("updateAt", time.Now().Format("15:04:05")),
					),
					h.Div(
						h.Class("update-time"),
						h.Text("最后更新: "),
						h.Span(
							h.ID("updateTime"),
							h.Text(time.Now().Format("15:04:05")),
						),
					),
				),
			)
		})
	})

	fmt.Println("服务器启动在 http://localhost:8080")
	fmt.Println("访问 http://localhost:8080 查看AG-Grid版本")

	if err := app.Start(); err != nil {
		fmt.Printf("服务器错误: %v\n", err)
	}
}
posted @ 2026-04-08 07:55  卓能文  阅读(2)  评论(0)    收藏  举报