/** * 运营监控页面 - 数据分析看板 */ import { useState, useEffect } from 'react' import { View, Text, ScrollView } from '@tarojs/components' import Taro from '@tarojs/taro' import { AtTabs, AtTabsPane, AtTag, AtActivityIndicator } from 'taro-ui' import { getOverviewStats, getRealtimeData, getPerformanceMetrics, getDeviceStats, getSourceStats, getPageStats, getRegionStats, } from '../../../../api/analytics' import type { OverviewStats, PerformanceMetric, DeviceStat, SourceStat, PageStat, RegionStat } from '../../../../types/analytics' import './analytics.scss' // 格式化数字 const formatNumber = (num?: number) => { if (!num) return '0' if (num >= 100000000) return (num / 100000000).toFixed(1) + '亿' if (num >= 10000) return (num / 10000).toFixed(1) + '万' return num.toLocaleString() } // 格式化百分比 const formatPercent = (num?: number) => { if (!num) return '0%' return num.toFixed(1) + '%' } export default function Analytics() { const [loading, setLoading] = useState(true) const [realtime, setRealtime] = useState({ uv: 0, pv: 0, apiCalls: 0, errors: 0, activeUsers: 0, }) const [overview, setOverview] = useState({}) const [metrics, setMetrics] = useState([]) const [devices, setDevices] = useState([]) const [sources, setSources] = useState([]) const [pages, setPages] = useState([]) const [regions, setRegions] = useState([]) const [activeTab, setActiveTab] = useState(0) const [appId, setAppId] = useState('') useEffect(() => { const pages = Taro.getCurrentPages() const current = pages[pages.length - 1] if (current?.options?.appId) { setAppId(current.options.appId) } }, []) useEffect(() => { if (appId) { fetchData() // 实时数据每 30 秒刷新 const timer = setInterval(() => { fetchRealtimeData() }, 30000) return () => clearInterval(timer) } }, [appId]) // 获取所有数据 const fetchData = async () => { try { setLoading(true) await Promise.all([ fetchRealtimeData(), fetchOverviewData(), fetchMetricsData(), fetchDeviceData(), fetchSourceData(), fetchPageData(), fetchRegionData(), ]) } catch (err) { console.error('获取数据失败', err) } finally { setLoading(false) } } // 获取实时数据 const fetchRealtimeData = async () => { try { const res = await getRealtimeData(Number(appId)) if (res.data) { setRealtime(res.data) } } catch (err) { console.error('获取实时数据失败', err) } } // 获取概览数据 const fetchOverviewData = async () => { try { const res = await getOverviewStats(Number(appId)) if (res.data) { setOverview(res.data) } } catch (err) { console.error('获取概览数据失败', err) } } // 获取性能指标 const fetchMetricsData = async () => { try { const res = await getPerformanceMetrics(Number(appId)) if (res.data) { setMetrics(res.data.list || []) } } catch (err) { console.error('获取性能指标失败', err) } } // 获取设备分布 const fetchDeviceData = async () => { try { const res = await getDeviceStats(Number(appId)) if (res.data) { setDevices(res.data.list || []) } } catch (err) { console.error('获取设备数据失败', err) } } // 获取来源分布 const fetchSourceData = async () => { try { const res = await getSourceStats(Number(appId)) if (res.data) { setSources(res.data.list || []) } } catch (err) { console.error('获取来源数据失败', err) } } // 获取页面访问 const fetchPageData = async () => { try { const res = await getPageStats({ websiteId: Number(appId), limit: 10, }) if (res.data) { setPages(res.data.list || []) } } catch (err) { console.error('获取页面数据失败', err) } } // 获取区域分布 const fetchRegionData = async () => { try { const res = await getRegionStats(Number(appId)) if (res.data) { setRegions(res.data.list || []) } } catch (err) { console.error('获取区域数据失败', err) } } const tabs = [ { title: '概览' }, { title: '性能' }, { title: '用户' }, { title: '页面' }, ] return ( {loading && realtime.uv === 0 ? ( 加载中... ) : ( <> {/* 实时数据 */} 实时数据 LIVE {formatNumber(realtime.uv)} 实时 UV {formatNumber(realtime.pv)} 实时 PV {formatNumber(realtime.apiCalls)} API 调用 0 ? 'error' : ''}`}> {formatNumber(realtime.errors)} 错误数 {/* 今日概览 */} {formatNumber(overview.todayUv)} 今日 UV {formatNumber(overview.todayPv)} 今日 PV {formatNumber(overview.todayApiCalls)} API 调用 {/* Tab 切换 */} setActiveTab(index)} > {/* 趋势图表区 */} 数据趋势 趋势图表 可集成 ECharts 展示数据趋势 {/* 设备分布 */} 设备分布 {devices.length > 0 ? ( {devices.map((device, index) => ( {device.device} {formatPercent(device.percentage)} ))} ) : ( 暂无数据 )} {/* 性能指标 */} 核心指标 {metrics.length > 0 ? ( metrics.map((metric, index) => ( {metric.metric} {metric.value?.toFixed(2)} {metric.unit} {metric.trend === 'up' ? '↑' : metric.trend === 'down' ? '↓' : '→'} {Math.abs(metric.change || 0)}% )) ) : ( 暂无数据 )} {/* 错误统计 */} 错误统计 {overview.todayErrors || 0} 今日错误 0 严重错误 {/* 用户统计 */} {formatNumber(overview.activeUsers)} 活跃用户 {formatNumber(overview.newUsers)} 新增用户 {formatPercent(overview.retention)} 留存率 {/* 区域分布 */} 地域分布 {regions.length > 0 ? ( regions.slice(0, 5).map((region, index) => ( {index + 1} {region.region} {formatPercent(region.percentage)} )) ) : ( 暂无数据 )} {/* 来源分布 */} 流量来源 {sources.length > 0 ? ( sources.map((source, index) => ( {index + 1} {source.source} {formatPercent(source.percentage)} )) ) : ( 暂无数据 )} {/* 页面排行 */} 页面访问排行 {pages.length > 0 ? ( pages.map((page, index) => ( {index + 1} {page.title || page.path} {page.path} {formatNumber(page.pv)} PV {formatNumber(page.uv)} UV )) ) : ( 暂无数据 )} )} ) }