blob: f87ef7a6191876079658f5db30ef7c162be52ff6 [file] [log] [blame]
TRM-coding85e5c322025-06-18 19:49:21 +08001import React, { useState, useEffect } from 'react';
2import {
3 ResponsiveContainer, LineChart, Line,
4 XAxis, YAxis, Tooltip, CartesianGrid, Legend
5} from 'recharts';
6import { fetchSysCost } from '../api/posts_trm';
7
8function PerformanceLogs({ userId }) {
9 const [data, setData] = useState([]);
TRM-coding2a8fd602025-06-19 19:33:16 +080010 const [loading, setLoading] = useState(true);
trm9984ee52025-06-20 15:16:56 +000011 const [unauthorized, setUnauthorized] = useState(false);
TRM-coding85e5c322025-06-18 19:49:21 +080012
13 useEffect(() => {
TRM-coding2a8fd602025-06-19 19:33:16 +080014 setLoading(true);
TRM-coding85e5c322025-06-18 19:49:21 +080015 fetchSysCost(userId)
trm9984ee52025-06-20 15:16:56 +000016 .then(result => {
17 // 检查是否是权限错误
18 if (result && result.status === 'error' && result.message === 'Unauthorized') {
19 setUnauthorized(true);
20 setData([]);
21 return;
22 }
23
24 // 确保数据是数组格式
25 let list = [];
26 if (Array.isArray(result)) {
27 list = result;
28 } else if (Array.isArray(result.data)) {
29 list = result.data;
30 } else if (Array.isArray(result.syscost)) {
31 list = result.syscost;
32 }
33
TRM-coding882dc442025-06-18 20:13:21 +080034 const msList = list.map(item => ({
35 ...item,
36 elapsed_time: item.elapsed_time * 1000,
37 cpu_user: item.cpu_user * 1000,
38 cpu_system: item.cpu_system * 1000,
39 memory_rss: item.memory_rss / (1024 * 1024*8), // convert bytes to MB
40 record_time_ts: new Date(item.record_time).getTime() // add numeric timestamp
41 }));
42 console.log('Converted data:', msList[0]); // debug first item
43 setData(msList);
trm9984ee52025-06-20 15:16:56 +000044 setUnauthorized(false);
TRM-coding882dc442025-06-18 20:13:21 +080045 })
trm9984ee52025-06-20 15:16:56 +000046 .catch(err => {
47 console.error('fetchSysCost error:', err);
48 if (err.message === 'Unauthorized') {
49 setUnauthorized(true);
50 setData([]);
51 }
52 })
TRM-coding2a8fd602025-06-19 19:33:16 +080053 .finally(() => setLoading(false));
TRM-coding85e5c322025-06-18 19:49:21 +080054 }, [userId]);
55
TRM-coding2a8fd602025-06-19 19:33:16 +080056 if (loading) {
57 return (
58 <section className="dashboard-performance">
59 <div style={{
60 display: 'flex',
61 justifyContent: 'center',
62 alignItems: 'center',
63 height: '400px',
64 flexDirection: 'column'
65 }}>
66 <div style={{
67 border: '4px solid #f3f3f3',
68 borderTop: '4px solid #3498db',
69 borderRadius: '50%',
70 width: '50px',
71 height: '50px',
72 animation: 'spin 1s linear infinite'
73 }}></div>
74 <p style={{ marginTop: '20px', color: '#666' }}>加载中...</p>
75 <style>{`
76 @keyframes spin {
77 0% { transform: rotate(0deg); }
78 100% { transform: rotate(360deg); }
79 }
80 `}</style>
81 </div>
82 </section>
83 );
84 }
85
trm9984ee52025-06-20 15:16:56 +000086 if (unauthorized) {
87 return (
88 <section className="dashboard-performance">
89 <div style={{
90 display: 'flex',
91 justifyContent: 'center',
92 alignItems: 'center',
93 height: '400px',
94 flexDirection: 'column',
95 textAlign: 'center'
96 }}>
97 <div style={{
98 fontSize: '18px',
99 color: '#ff4d4f',
100 marginBottom: '10px'
101 }}>
102 权限不足,无法访问性能监控数据
103 </div>
104 <div style={{
105 fontSize: '14px',
106 color: '#666'
107 }}>
108 请联系管理员获取相应权限
109 </div>
110 </div>
111 </section>
112 );
113 }
114
TRM-coding85e5c322025-06-18 19:49:21 +0800115 return (
116 <section className="dashboard-performance">
TRM-coding882dc442025-06-18 20:13:21 +0800117 {/* 响应时间图表 */}
118 <div style={{ marginBottom: '30px' }}>
119 <h3>响应时间</h3>
120 <ResponsiveContainer width="100%" height={300}>
121 <LineChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
122 <CartesianGrid strokeDasharray="3 3" />
123 <XAxis
124 dataKey="record_time_ts"
125 type="number"
126 domain={['dataMin', 'dataMax']}
127 tickFormatter={ts => new Date(ts).toLocaleTimeString()}
128 />
129 <YAxis domain={[0, 'auto']} label={{ value: '时间 (ms)', angle: -90, position: 'insideLeft' }} />
130 <Tooltip formatter={(val) => typeof val === 'number' ? `${val.toFixed(2)} ms` : val} />
131 <Legend />
132 <Line type="monotone" dataKey="elapsed_time" stroke="#8884d8" name="响应时间 (ms)" />
133 </LineChart>
134 </ResponsiveContainer>
135 </div>
136
137 {/* CPU时间图表 */}
138 <div style={{ marginBottom: '30px' }}>
139 <h3>CPU 使用时间</h3>
140 <ResponsiveContainer width="100%" height={300}>
141 <LineChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
142 <CartesianGrid strokeDasharray="3 3" />
143 <XAxis
144 dataKey="record_time_ts"
145 type="number"
146 domain={['dataMin', 'dataMax']}
147 tickFormatter={ts => new Date(ts).toLocaleTimeString()}
148 />
149 <YAxis domain={[0, 'auto']} label={{ value: '时间 (ms)', angle: -90, position: 'insideLeft' }} />
150 <Tooltip formatter={(val) => typeof val === 'number' ? `${val.toFixed(2)} ms` : val} />
151 <Legend />
152 <Line type="monotone" dataKey="cpu_user" stroke="#82ca9d" name="CPU 用户时间 (ms)" />
153 <Line type="monotone" dataKey="cpu_system" stroke="#ffc658" name="CPU 系统时间 (ms)" />
154 </LineChart>
155 </ResponsiveContainer>
156 </div>
157
158 {/* 内存图表 */}
159 <div style={{ marginBottom: '30px' }}>
160 <h3>内存使用</h3>
161 <ResponsiveContainer width="100%" height={300}>
162 <LineChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
163 <CartesianGrid strokeDasharray="3 3" />
164 <XAxis
165 dataKey="record_time_ts"
166 type="number"
167 domain={['dataMin', 'dataMax']}
168 tickFormatter={ts => new Date(ts).toLocaleTimeString()}
169 />
170 <YAxis domain={[0, 'auto']} label={{ value: '内存 (MB)', angle: -90, position: 'insideLeft' }} />
171 <Tooltip formatter={(val) => typeof val === 'number' ? `${val.toFixed(2)} MB` : val} />
172 <Legend />
173 <Line type="monotone" dataKey="memory_rss" stroke="#ff7300" name="内存 RSS" />
174 </LineChart>
175 </ResponsiveContainer>
176 </div>
TRM-coding85e5c322025-06-18 19:49:21 +0800177 </section>
178 );
179}
180
181export default PerformanceLogs;