修复令牌验证逻辑,修改管理员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>