修复令牌验证逻辑,修改管理员dashboard,增加退出登录功能

Change-Id: I6a832763126dffd28733269044a1b1956c5b1106
diff --git a/Merge/front/src/components/PerformanceLogs.js b/Merge/front/src/components/PerformanceLogs.js
index be9eb99..00f7e7d 100644
--- a/Merge/front/src/components/PerformanceLogs.js
+++ b/Merge/front/src/components/PerformanceLogs.js
@@ -7,8 +7,10 @@
 
 function PerformanceLogs({ userId }) {
   const [data, setData] = useState([]);
+  const [loading, setLoading] = useState(true);
 
   useEffect(() => {
+    setLoading(true);
     fetchSysCost(userId)
       .then(list => {
         const msList = list.map(item => ({
@@ -22,9 +24,40 @@
         console.log('Converted data:', msList[0]); // debug first item
         setData(msList);
       })
-      .catch(err => console.error('fetchSysCost error:', err));
+      .catch(err => console.error('fetchSysCost error:', err))
+      .finally(() => setLoading(false));
   }, [userId]);
 
+  if (loading) {
+    return (
+      <section className="dashboard-performance">
+        <div style={{ 
+          display: 'flex', 
+          justifyContent: 'center', 
+          alignItems: 'center', 
+          height: '400px',
+          flexDirection: 'column'
+        }}>
+          <div style={{
+            border: '4px solid #f3f3f3',
+            borderTop: '4px solid #3498db',
+            borderRadius: '50%',
+            width: '50px',
+            height: '50px',
+            animation: 'spin 1s linear infinite'
+          }}></div>
+          <p style={{ marginTop: '20px', color: '#666' }}>加载中...</p>
+          <style>{`
+            @keyframes spin {
+              0% { transform: rotate(0deg); }
+              100% { transform: rotate(360deg); }
+            }
+          `}</style>
+        </div>
+      </section>
+    );
+  }
+
   return (
     <section className="dashboard-performance">
       {/* 响应时间图表 */}
diff --git a/Merge/front/src/components/TransactionLogs.js b/Merge/front/src/components/TransactionLogs.js
index 9df8f67..24a31cb 100644
--- a/Merge/front/src/components/TransactionLogs.js
+++ b/Merge/front/src/components/TransactionLogs.js
@@ -2,14 +2,76 @@
 import { fetchRecordLog } from '../api/posts_trm';
 
 function TransactionLogs({ userId }) {
-  const [records, setRecords] = useState([]);
+  const [allRecords, setAllRecords] = useState([]);
+  const [currentPage, setCurrentPage] = useState(1);
+  const [pageSize] = useState(10);
+  const [loading, setLoading] = useState(false);
+
+  const loadRecords = async () => {
+    setLoading(true);
+    try {
+      const data = await fetchRecordLog();
+      setAllRecords(data);
+    } catch (err) {
+      console.error('fetchRecordLog error:', err);
+    } finally {
+      setLoading(false);
+    }
+  };
 
   useEffect(() => {
-    fetchRecordLog(userId)
-      .then(data => setRecords(data))
-      .catch(err => console.error('fetchRecordLog error:', err));
+    loadRecords();
   }, [userId]);
 
+  const total = allRecords.length;
+  const totalPages = Math.ceil(total / pageSize);
+  
+  // 计算当前页显示的数据
+  const startIndex = (currentPage - 1) * pageSize;
+  const endIndex = startIndex + pageSize;
+  const currentRecords = allRecords.slice(startIndex, endIndex);
+
+  const handlePrevPage = () => {
+    if (currentPage > 1) {
+      setCurrentPage(currentPage - 1);
+    }
+  };
+
+  const handleNextPage = () => {
+    if (currentPage < totalPages) {
+      setCurrentPage(currentPage + 1);
+    }
+  };
+
+  const handlePageClick = (page) => {
+    setCurrentPage(page);
+  };
+
+  const renderPageNumbers = () => {
+    const pages = [];
+    const maxVisiblePages = 5;
+    
+    let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));
+    let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
+    
+    if (endPage - startPage < maxVisiblePages - 1) {
+      startPage = Math.max(1, endPage - maxVisiblePages + 1);
+    }
+
+    for (let i = startPage; i <= endPage; i++) {
+      pages.push(
+        <button
+          key={i}
+          onClick={() => handlePageClick(i)}
+          className={`page-btn ${currentPage === i ? 'active' : ''}`}
+        >
+          {i}
+        </button>
+      );
+    }
+    return pages;
+  };
+
   return (
     <section className="dashboard-logs">
       <table className="admin-table">
@@ -24,25 +86,96 @@
           </tr>
         </thead>
         <tbody>
-          {records.length > 0
-            ? records.map((r, i) => (
-                <tr key={i}>
-                  <td>{r.id}</td>
-                  <td>{r.user_id}</td>
-                  <td>{r.type}</td>
-                  <td>{r.content}</td>
-                  <td>{r.ip}</td>
-                  <td>{new Date(r.created_at).toLocaleString()}</td>
-                </tr>
-              ))
-            : (
-              <tr>
-                <td colSpan="6" style={{ textAlign: 'center' }}>暂无数据</td>
+          {loading ? (
+            <tr>
+              <td colSpan="6" style={{ textAlign: 'center' }}>加载中...</td>
+            </tr>
+          ) : currentRecords.length > 0 ? (
+            currentRecords.map((r, i) => (
+              <tr key={r.id || i}>
+                <td>{r.id}</td>
+                <td>{r.user_id}</td>
+                <td>{r.type}</td>
+                <td>{r.content}</td>
+                <td>{r.ip}</td>
+                <td>{new Date(r.created_at).toLocaleString()}</td>
               </tr>
-            )
-          }
+            ))
+          ) : (
+            <tr>
+              <td colSpan="6" style={{ textAlign: 'center' }}>暂无数据</td>
+            </tr>
+          )}
         </tbody>
       </table>
+      
+      {totalPages > 1 && (
+        <div className="pagination">
+          <button 
+            onClick={handlePrevPage} 
+            disabled={currentPage === 1}
+            className="page-btn"
+          >
+            上一页
+          </button>
+          
+          {renderPageNumbers()}
+          
+          <button 
+            onClick={handleNextPage} 
+            disabled={currentPage === totalPages}
+            className="page-btn"
+          >
+            下一页
+          </button>
+          
+          <span className="page-info">
+            第 {currentPage} 页,共 {totalPages} 页,总计 {total} 条记录
+          </span>
+        </div>
+      )}
+      
+      <style jsx>{`
+        .pagination {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          gap: 8px;
+          margin-top: 16px;
+          flex-wrap: wrap;
+        }
+        
+        .page-btn {
+          padding: 6px 12px;
+          border: 1px solid #ddd;
+          background: white;
+          cursor: pointer;
+          border-radius: 4px;
+          transition: all 0.2s;
+        }
+        
+        .page-btn:hover:not(:disabled) {
+          background: #f5f5f5;
+          border-color: #999;
+        }
+        
+        .page-btn:disabled {
+          opacity: 0.5;
+          cursor: not-allowed;
+        }
+        
+        .page-btn.active {
+          background: #007bff;
+          color: white;
+          border-color: #007bff;
+        }
+        
+        .page-info {
+          margin-left: 16px;
+          font-size: 14px;
+          color: #666;
+        }
+      `}</style>
     </section>
   );
 }
diff --git a/Merge/front/src/components/UserProfile.jsx b/Merge/front/src/components/UserProfile.jsx
index 8421997..53618db 100644
--- a/Merge/front/src/components/UserProfile.jsx
+++ b/Merge/front/src/components/UserProfile.jsx
@@ -29,7 +29,8 @@
   useTheme,
   CircularProgress,
   Snackbar,
-  Alert
+  Alert,
+  Menu
 } from '@mui/material';
 import { useParams } from 'react-router-dom';
 import { 
@@ -123,6 +124,7 @@
   const [isEditing, setIsEditing] = useState(false);
   const [followers, setFollowers] = useState([]);
   const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
+  const [anchorEl, setAnchorEl] = useState(null);
   
   // 用户数据状态
   const [currentUser, setCurrentUser] = useState(null);
@@ -149,6 +151,8 @@
     location: ''
   });
 
+  const menuOpen = Boolean(anchorEl);
+
   // 显示提示信息
   const showSnackbar = (message, severity = 'success') => {
     setSnackbar({ open: true, message, severity });
@@ -390,6 +394,16 @@
     navigate(`/user/${userId}`);
   };
 
+  const handleMenuOpen = (e) => setAnchorEl(e.currentTarget);
+  const handleMenuClose = () => setAnchorEl(null);
+  const handleLogout = () => {
+    handleMenuClose();
+    // 清理本地存储
+    localStorage.clear();
+    // 在此处添加退出登录逻辑,比如清理本地存储并跳转
+    navigate('/login');
+  };
+
   if (loading) {
     return (
       <Box sx={{ 
@@ -531,9 +545,18 @@
                         >
                           {profileUser.is_following ? '已关注' : '关注'}
                         </Button>
-                        <IconButton sx={{ ml: 1 }}>
+                        <IconButton sx={{ ml: 1 }} onClick={handleMenuOpen}>
                           <MoreVert />
                         </IconButton>
+                        <Menu
+                          anchorEl={anchorEl}
+                          open={menuOpen}
+                          onClose={handleMenuClose}
+                          anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
+                          transformOrigin={{ vertical: 'top', horizontal: 'right' }}
+                        >
+                          <MenuItem onClick={handleLogout}>退出登录</MenuItem>
+                        </Menu>
                       </>
                     )}
                   </Box>