From 2af47ee162744f20f7ab9cf04c234f24da32c2fc Mon Sep 17 00:00:00 2001 From: zhang1106 <849185023@qq.com> Date: Tue, 20 Jan 2026 16:54:26 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=8E=86=E5=8F=B2=E5=92=8C=E6=93=8D=E4=BD=9C=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=8A=9F=E8=83=BD=E5=8F=8A=E7=9B=B8=E5=85=B3=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除后端登录历史和操作日志的模型、路由及前端相关页面和路由配置 清理创建和删除索引脚本中的相关代码 调整系统管理菜单结构 --- backend/create_indexes.js | 22 +- backend/middleware/auth.js | 1 - backend/models/LoginHistory.js | 63 ----- backend/models/OperationLog.js | 89 ------- backend/routes/loginHistory.js | 120 ---------- backend/routes/operationLogs.js | 162 ------------- backend/server.js | 4 - frontend/src/App.jsx | 48 ++-- frontend/src/pages/LoginHistory.jsx | 194 ---------------- frontend/src/pages/OperationLogs.jsx | 335 --------------------------- 10 files changed, 18 insertions(+), 1020 deletions(-) delete mode 100644 backend/models/LoginHistory.js delete mode 100644 backend/models/OperationLog.js delete mode 100644 backend/routes/loginHistory.js delete mode 100644 backend/routes/operationLogs.js delete mode 100644 frontend/src/pages/LoginHistory.jsx delete mode 100644 frontend/src/pages/OperationLogs.jsx diff --git a/backend/create_indexes.js b/backend/create_indexes.js index 0f9fe08..07d9a4e 100644 --- a/backend/create_indexes.js +++ b/backend/create_indexes.js @@ -45,23 +45,6 @@ const createIndexes = async () => { await queryInterface.addIndex('consumable_logs', ['consumableId', 'createdAt']); console.log(' ✓ consumable_logs 表索引创建完成'); - // OperationLog 表索引 - console.log('创建 operation_logs 表索引...'); - await queryInterface.addIndex('operation_logs', ['userId']); - await queryInterface.addIndex('operation_logs', ['action']); - await queryInterface.addIndex('operation_logs', ['module']); - await queryInterface.addIndex('operation_logs', ['createdAt']); - await queryInterface.addIndex('operation_logs', ['userId', 'createdAt']); - console.log(' ✓ operation_logs 表索引创建完成'); - - // LoginHistory 表索引 - console.log('创建 login_histories 表索引...'); - await queryInterface.addIndex('login_histories', ['userId']); - await queryInterface.addIndex('login_histories', ['loginTime']); - await queryInterface.addIndex('login_histories', ['loginType']); - await queryInterface.addIndex('login_histories', ['userId', 'loginTime']); - console.log(' ✓ login_histories 表索引创建完成'); - // Rack 表索引 console.log('创建 racks 表索引...'); await queryInterface.addIndex('racks', ['roomId']); @@ -87,8 +70,7 @@ const checkIndexes = async () => { const queryInterface = sequelize.getQueryInterface(); const tables = [ 'devices', 'users', 'consumables', 'consumable_records', - 'consumable_logs', 'operation_logs', 'login_histories', - 'racks', 'rooms' + 'consumable_logs', 'racks', 'rooms' ]; console.log('\n检查现有索引...'); @@ -117,8 +99,6 @@ const dropIndexes = async () => { { table: 'consumables', indexes: ['category', 'status', 'category_status'] }, { table: 'consumable_records', indexes: ['consumableId', 'type', 'createdAt'] }, { table: 'consumable_logs', indexes: ['consumableId', 'operationType', 'createdAt', 'consumableId_createdAt'] }, - { table: 'operation_logs', indexes: ['userId', 'action', 'module', 'createdAt', 'userId_createdAt'] }, - { table: 'login_histories', indexes: ['userId', 'loginTime', 'loginType', 'userId_loginTime'] }, { table: 'racks', indexes: ['roomId', 'status', 'roomId_status'] }, { table: 'rooms', indexes: ['status', 'name'] } ]; diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js index db9e6ff..77a1c6b 100644 --- a/backend/middleware/auth.js +++ b/backend/middleware/auth.js @@ -1,6 +1,5 @@ const jwt = require('jsonwebtoken'); const User = require('../models/User'); -const LoginHistory = require('../models/LoginHistory'); const JWT_SECRET = process.env.JWT_SECRET || 'idc-management-secret-key-2024'; const TOKEN_EXPIRY = process.env.TOKEN_EXPIRY || '24h'; diff --git a/backend/models/LoginHistory.js b/backend/models/LoginHistory.js deleted file mode 100644 index 11ec7de..0000000 --- a/backend/models/LoginHistory.js +++ /dev/null @@ -1,63 +0,0 @@ -const { DataTypes } = require('sequelize'); -const { sequelize } = require('../db'); - -const LoginHistory = sequelize.define('LoginHistory', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - userId: { - type: DataTypes.STRING, - allowNull: false, - comment: '用户ID' - }, - username: { - type: DataTypes.STRING, - allowNull: false, - comment: '用户名' - }, - realName: { - type: DataTypes.STRING, - allowNull: true, - comment: '真实姓名' - }, - loginTime: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - comment: '登录时间' - }, - loginIp: { - type: DataTypes.STRING, - allowNull: true, - comment: '登录IP' - }, - userAgent: { - type: DataTypes.TEXT, - allowNull: true, - comment: '浏览器UA' - }, - loginType: { - type: DataTypes.ENUM('success', 'failed'), - defaultValue: 'success', - comment: '登录结果' - }, - failReason: { - type: DataTypes.STRING, - allowNull: true, - comment: '失败原因' - } -}, { - tableName: 'login_histories', - timestamps: true, - createdAt: 'loginTime', - updatedAt: false, - indexes: [ - { fields: ['userId'] }, - { fields: ['loginTime'] }, - { fields: ['loginType'] }, - { fields: ['userId', 'loginTime'] } - ] -}); - -module.exports = LoginHistory; diff --git a/backend/models/OperationLog.js b/backend/models/OperationLog.js deleted file mode 100644 index 629471b..0000000 --- a/backend/models/OperationLog.js +++ /dev/null @@ -1,89 +0,0 @@ -const { DataTypes } = require('sequelize'); -const { sequelize } = require('../db'); - -const OperationLog = sequelize.define('OperationLog', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - userId: { - type: DataTypes.STRING, - allowNull: false, - comment: '操作人ID' - }, - username: { - type: DataTypes.STRING, - allowNull: false, - comment: '操作人用户名' - }, - realName: { - type: DataTypes.STRING, - allowNull: true, - comment: '操作人真实姓名' - }, - action: { - type: DataTypes.STRING, - allowNull: false, - comment: '操作类型' - }, - module: { - type: DataTypes.STRING, - allowNull: true, - comment: '操作模块' - }, - description: { - type: DataTypes.TEXT, - allowNull: true, - comment: '操作描述' - }, - targetId: { - type: DataTypes.STRING, - allowNull: true, - comment: '目标对象ID' - }, - targetName: { - type: DataTypes.STRING, - allowNull: true, - comment: '目标对象名称' - }, - oldValue: { - type: DataTypes.TEXT, - allowNull: true, - comment: '旧值' - }, - newValue: { - type: DataTypes.TEXT, - allowNull: true, - comment: '新值' - }, - ip: { - type: DataTypes.STRING, - allowNull: true, - comment: '操作IP' - }, - status: { - type: DataTypes.ENUM('success', 'failed'), - defaultValue: 'success', - comment: '操作状态' - }, - errorMessage: { - type: DataTypes.TEXT, - allowNull: true, - comment: '错误信息' - } -}, { - tableName: 'operation_logs', - timestamps: true, - createdAt: 'operateTime', - updatedAt: false, - indexes: [ - { fields: ['userId'] }, - { fields: ['action'] }, - { fields: ['module'] }, - { fields: ['operateTime'] }, - { fields: ['userId', 'operateTime'] } - ] -}); - -module.exports = OperationLog; diff --git a/backend/routes/loginHistory.js b/backend/routes/loginHistory.js deleted file mode 100644 index 51434ca..0000000 --- a/backend/routes/loginHistory.js +++ /dev/null @@ -1,120 +0,0 @@ -const express = require('express'); -const LoginHistory = require('../models/LoginHistory'); -const User = require('../models/User'); -const { authMiddleware } = require('../middleware/auth'); - -const router = express.Router(); - -router.get('/', authMiddleware, async (req, res) => { - try { - const { page = 1, pageSize = 10, userId, loginType, startDate, endDate } = req.query; - const offset = (parseInt(page) - 1) * parseInt(pageSize); - const limit = parseInt(pageSize); - const where = {}; - - if (userId) where.userId = userId; - if (loginType) where.loginType = loginType; - if (startDate || endDate) { - where.loginTime = {}; - if (startDate) where.loginTime[Op.gte] = new Date(startDate); - if (endDate) where.loginTime[Op.lte] = new Date(endDate); - } - - const { count, rows } = await LoginHistory.findAndCountAll({ - where, - limit, - offset, - order: [['loginTime', 'DESC']] - }); - - res.json({ - success: true, - data: { - total: count, - page: parseInt(page), - pageSize: parseInt(pageSize), - histories: rows.map(h => ({ - id: h.id, - userId: h.userId, - username: h.username, - realName: h.realName, - loginTime: h.loginTime, - loginIp: h.loginIp, - userAgent: h.userAgent, - loginType: h.loginType, - failReason: h.failReason - })) - } - }); - } catch (error) { - console.error('获取登录历史错误:', error); - res.status(500).json({ - success: false, - message: '获取登录历史失败' - }); - } -}); - -router.get('/user/:userId', authMiddleware, async (req, res) => { - try { - const { page = 1, pageSize = 10 } = req.query; - const offset = (parseInt(page) - 1) * parseInt(pageSize); - const limit = parseInt(pageSize); - - const { count, rows } = await LoginHistory.findAndCountAll({ - where: { userId: req.params.userId }, - limit, - offset, - order: [['loginTime', 'DESC']] - }); - - res.json({ - success: true, - data: { - total: count, - page: parseInt(page), - pageSize: parseInt(pageSize), - histories: rows - } - }); - } catch (error) { - console.error('获取用户登录历史错误:', error); - res.status(500).json({ - success: false, - message: '获取登录历史失败' - }); - } -}); - -router.delete('/:id', authMiddleware, async (req, res) => { - try { - await LoginHistory.destroy({ where: { id: req.params.id } }); - res.json({ success: true, message: '删除成功' }); - } catch (error) { - console.error('删除登录历史错误:', error); - res.status(500).json({ success: false, message: '删除失败' }); - } -}); - -router.delete('/', authMiddleware, async (req, res) => { - try { - const { days } = req.body; - const where = { loginType: 'success' }; - - if (days) { - const cutoffDate = new Date(); - cutoffDate.setDate(cutoffDate.getDate() - days); - where.loginTime = { [Op.lt]: cutoffDate }; - } - - await LoginHistory.destroy({ where }); - res.json({ success: true, message: '清理成功' }); - } catch (error) { - console.error('清理登录历史错误:', error); - res.status(500).json({ success: false, message: '清理失败' }); - } -}); - -const { Op } = require('sequelize'); - -module.exports = router; diff --git a/backend/routes/operationLogs.js b/backend/routes/operationLogs.js deleted file mode 100644 index 2ceb94c..0000000 --- a/backend/routes/operationLogs.js +++ /dev/null @@ -1,162 +0,0 @@ -const express = require('express'); -const OperationLog = require('../models/OperationLog'); -const { authMiddleware } = require('../middleware/auth'); - -const router = express.Router(); - -const ACTION_TYPES = { - USER_CREATE: '创建用户', - USER_UPDATE: '修改用户', - USER_DELETE: '删除用户', - USER_LOGIN: '用户登录', - USER_LOGOUT: '用户登出', - ROLE_CREATE: '创建角色', - ROLE_UPDATE: '修改角色', - ROLE_DELETE: '删除角色', - ROLE_ASSIGN: '分配角色', - DEVICE_CREATE: '创建设备', - DEVICE_UPDATE: '修改设备', - DEVICE_DELETE: '删除设备', - CONSUMABLE_IN: '耗材入库', - CONSUMABLE_OUT: '耗材出库', - CONSUMABLE_RECORD: '耗材记录', - SYSTEM_CONFIG: '系统配置' -}; - -router.get('/', authMiddleware, async (req, res) => { - try { - const { page = 1, pageSize = 20, userId, action, module, startDate, endDate } = req.query; - const offset = (parseInt(page) - 1) * parseInt(pageSize); - const limit = parseInt(pageSize); - const where = {}; - - if (userId) where.userId = userId; - if (action) where.action = action; - if (module) where.module = module; - if (startDate || endDate) { - where.operateTime = {}; - if (startDate) where.operateTime[Op.gte] = new Date(startDate); - if (endDate) where.operateTime[Op.lte] = new Date(endDate); - } - - const { count, rows } = await OperationLog.findAndCountAll({ - where, - limit, - offset, - order: [['operateTime', 'DESC']] - }); - - res.json({ - success: true, - data: { - total: count, - page: parseInt(page), - pageSize: parseInt(pageSize), - logs: rows.map(l => ({ - id: l.id, - userId: l.userId, - username: l.username, - realName: l.realName, - action: l.action, - module: l.module, - description: l.description, - targetId: l.targetId, - targetName: l.targetName, - oldValue: l.oldValue, - newValue: l.newValue, - ip: l.ip, - status: l.status, - errorMessage: l.errorMessage, - operateTime: l.operateTime - })) - } - }); - } catch (error) { - console.error('获取操作日志错误:', error); - res.status(500).json({ - success: false, - message: '获取操作日志失败' - }); - } -}); - -router.get('/actions', authMiddleware, (req, res) => { - res.json({ - success: true, - data: Object.entries(ACTION_TYPES).map(([key, value]) => ({ - key, - value, - label: value - })) - }); -}); - -router.get('/modules', authMiddleware, (req, res) => { - res.json({ - success: true, - data: [ - { key: 'user', value: 'user', label: '用户管理' }, - { key: 'role', value: 'role', label: '角色管理' }, - { key: 'device', value: 'device', label: '设备管理' }, - { key: 'consumable', value: 'consumable', label: '耗材管理' }, - { key: 'system', value: 'system', label: '系统设置' } - ] - }); -}); - -router.delete('/:id', authMiddleware, async (req, res) => { - try { - await OperationLog.destroy({ where: { id: req.params.id } }); - res.json({ success: true, message: '删除成功' }); - } catch (error) { - console.error('删除操作日志错误:', error); - res.status(500).json({ success: false, message: '删除失败' }); - } -}); - -router.delete('/', authMiddleware, async (req, res) => { - try { - const { days } = req.body; - const where = {}; - - if (days) { - const cutoffDate = new Date(); - cutoffDate.setDate(cutoffDate.getDate() - days); - where.operateTime = { [Op.lt]: cutoffDate }; - } - - await OperationLog.destroy({ where }); - res.json({ success: true, message: '清理成功' }); - } catch (error) { - console.error('清理操作日志错误:', error); - res.status(500).json({ success: false, message: '清理失败' }); - } -}); - -const logOperation = async (req, action, module, description, targetId, targetName, oldValue, newValue, status = 'success', errorMessage = null) => { - try { - await OperationLog.create({ - userId: req.user?.userId, - username: req.user?.username, - realName: req.userModel?.realName, - action, - module, - description, - targetId, - targetName, - oldValue: oldValue ? JSON.stringify(oldValue) : null, - newValue: newValue ? JSON.stringify(newValue) : null, - ip: req.ip, - status, - errorMessage - }); - } catch (error) { - console.error('记录操作日志错误:', error); - } -}; - -module.exports = router; -module.exports.ACTION_TYPES = ACTION_TYPES; -module.exports.logOperation = logOperation; - -const { Op } = require('sequelize'); diff --git a/backend/server.js b/backend/server.js index b59de09..772181d 100644 --- a/backend/server.js +++ b/backend/server.js @@ -58,8 +58,6 @@ const consumableCategoryRoutes = require('./routes/consumableCategories'); const authRoutes = require('./routes/auth'); const usersRoutes = require('./routes/users'); const rolesRoutes = require('./routes/roles'); -const loginHistoryRoutes = require('./routes/loginHistory'); -const operationLogsRoutes = require('./routes/operationLogs'); const ticketRoutes = require('./routes/tickets'); const ticketCategoryRoutes = require('./routes/ticketCategories'); const ticketFieldRoutes = require('./routes/ticketFields'); @@ -77,8 +75,6 @@ app.use('/api/consumable-categories', consumableCategoryRoutes); app.use('/api/auth', authRoutes); app.use('/api/users', usersRoutes); app.use('/api/roles', rolesRoutes); -app.use('/api/login-history', loginHistoryRoutes); -app.use('/api/operation-logs', operationLogsRoutes); app.use('/api/tickets', ticketRoutes); app.use('/api/ticket-categories', ticketCategoryRoutes); app.use('/api/ticket-fields', ticketFieldRoutes); diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 168fbcb..6c6b2b7 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -16,8 +16,6 @@ const ConsumableStatistics = lazy(() => import('./pages/ConsumableStatistics')); const ConsumableLogs = lazy(() => import('./pages/ConsumableLogs')); const CategoryManagement = lazy(() => import('./pages/CategoryManagement')); const UserManagement = lazy(() => import('./pages/UserManagement')); -const LoginHistory = lazy(() => import('./pages/LoginHistory')); -const OperationLogs = lazy(() => import('./pages/OperationLogs')); const Login = lazy(() => import('./pages/Login')); const TicketManagement = lazy(() => import('./pages/TicketManagement')); const TicketCategoryManagement = lazy(() => import('./pages/TicketCategoryManagement')); @@ -223,33 +221,6 @@ const AppLayout = ({ children }) => { }, ], }, - { - key: 'system-management', - icon: , - label: '系统管理', - children: [ - { - key: 'users', - icon: , - label: 用户管理, - }, - { - key: 'login-history', - icon: , - label: 登录历史, - }, - { - key: 'operation-logs', - icon: , - label: 操作日志, - }, - { - key: 'system-settings', - icon: , - label: 系统设置, - }, - ], - }, { key: 'ticket-management', icon: , @@ -277,6 +248,23 @@ const AppLayout = ({ children }) => { }, ], }, + { + key: 'system-management', + icon: , + label: '系统管理', + children: [ + { + key: 'users', + icon: , + label: 用户管理, + }, + { + key: 'system-settings', + icon: , + label: 系统设置, + }, + ], + }, ]; return ( @@ -480,8 +468,6 @@ function App() { } /> } /> } /> - } /> - } /> } /> } /> } /> diff --git a/frontend/src/pages/LoginHistory.jsx b/frontend/src/pages/LoginHistory.jsx deleted file mode 100644 index 438078f..0000000 --- a/frontend/src/pages/LoginHistory.jsx +++ /dev/null @@ -1,194 +0,0 @@ -import React, { useState, useEffect, useCallback, useMemo } from 'react'; -import { Card, Table, Tag, Space, Button, DatePicker, Select, message, Popconfirm, Typography, Descriptions } from 'antd'; -import { ReloadOutlined, DeleteOutlined, EyeOutlined, SafetyCertificateOutlined } from '@ant-design/icons'; -import { loginHistoryAPI } from '../api'; -import dayjs from 'dayjs'; - -const { Title } = Typography; -const { RangePicker } = DatePicker; - -const LoginHistory = () => { - const [histories, setHistories] = useState([]); - const [loading, setLoading] = useState(false); - const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); - const [filters, setFilters] = useState({}); - - useEffect(() => { - fetchHistories(); - }, [pagination.current, filters]); - - const fetchHistories = useCallback(async () => { - setLoading(true); - try { - const params = { - page: pagination.current, - pageSize: pagination.pageSize, - ...filters - }; - const response = await loginHistoryAPI.list(params); - if (response.success) { - setHistories(response.data.histories); - setPagination(prev => ({ ...prev, total: response.data.total })); - } - } catch (error) { - message.error('获取登录历史失败'); - } finally { - setLoading(false); - } - }, [pagination.current, pagination.pageSize, filters]); - - const handleFilterChange = useCallback((key, value) => { - setFilters(prev => ({ ...prev, [key]: value })); - setPagination(prev => ({ ...prev, current: 1 })); - }, []); - - const handleDateChange = useCallback((dates) => { - if (dates) { - setFilters(prev => ({ - ...prev, - startDate: dates[0].toISOString(), - endDate: dates[1].toISOString() - })); - } else { - setFilters(prev => ({ ...prev, startDate: undefined, endDate: undefined })); - } - setPagination(prev => ({ ...prev, current: 1 })); - }, []); - - const handleClear = useCallback(async () => { - try { - const response = await loginHistoryAPI.clear({ days: 30 }); - if (response.success) { - message.success('已清理30天前的登录记录'); - fetchHistories(); - } - } catch (error) { - message.error('清理失败'); - } - }, [fetchHistories]); - - const tableColumns = useMemo(() => [ - { - title: '用户名', - dataIndex: 'username', - key: 'username', - width: 120 - }, - { - title: '真实姓名', - dataIndex: 'realName', - key: 'realName', - width: 100, - render: (name) => name || '-' - }, - { - title: '登录时间', - dataIndex: 'loginTime', - key: 'loginTime', - width: 180, - render: (time) => time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '-' - }, - { - title: 'IP地址', - dataIndex: 'loginIp', - key: 'loginIp', - width: 140, - render: (ip) => ip || '-' - }, - { - title: '登录状态', - dataIndex: 'loginType', - key: 'loginType', - width: 100, - render: (type) => ( - - {type === 'success' ? '成功' : '失败'} - - ) - }, - { - title: '失败原因', - dataIndex: 'failReason', - key: 'failReason', - width: 150, - render: (reason) => reason || '-' - }, - { - title: '浏览器', - dataIndex: 'userAgent', - key: 'userAgent', - ellipsis: true, - render: (ua) => { - if (!ua) return '-'; - let browser = 'Unknown'; - if (ua.includes('Chrome')) browser = 'Chrome'; - else if (ua.includes('Firefox')) browser = 'Firefox'; - else if (ua.includes('Safari')) browser = 'Safari'; - else if (ua.includes('Edge')) browser = 'Edge'; - return browser; - } - } - ], []); - - const pageHeaderStyle = { - marginBottom: '24px', - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center' - }; - - const titleStyle = { - fontSize: '20px', - fontWeight: '600', - margin: 0 - }; - - return ( - - - 登录历史 - - } onClick={fetchHistories}>刷新 - - 清理旧记录 - - - - - - - handleFilterChange('loginType', value)} - > - 成功 - 失败 - - - - - - - `共 ${total} 条记录` - }} - onChange={(newPagination) => { - setPagination(prev => ({ ...prev, ...newPagination })); - }} - /> - - - ); -}; - -export default LoginHistory; diff --git a/frontend/src/pages/OperationLogs.jsx b/frontend/src/pages/OperationLogs.jsx deleted file mode 100644 index dd9488e..0000000 --- a/frontend/src/pages/OperationLogs.jsx +++ /dev/null @@ -1,335 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Card, Table, Tag, Space, Button, DatePicker, Select, Input, message, Popconfirm, Typography, Drawer, Descriptions, Timeline } from 'antd'; -import { ReloadOutlined, DeleteOutlined, EyeOutlined, FileTextOutlined } from '@ant-design/icons'; -import { operationLogAPI } from '../api'; -import dayjs from 'dayjs'; - -const { Title } = Typography; -const { RangePicker } = DatePicker; - -const OperationLogs = () => { - const [logs, setLogs] = useState([]); - const [loading, setLoading] = useState(false); - const [pagination, setPagination] = useState({ current: 1, pageSize: 20, total: 0 }); - const [filters, setFilters] = useState({}); - const [actions, setActions] = useState([]); - const [modules, setModules] = useState([]); - const [detailVisible, setDetailVisible] = useState(false); - const [selectedLog, setSelectedLog] = useState(null); - - useEffect(() => { - fetchLogs(); - fetchOptions(); - }, [pagination.current, filters]); - - const fetchLogs = async () => { - setLoading(true); - try { - const params = { - page: pagination.current, - pageSize: pagination.pageSize, - ...filters - }; - const response = await operationLogAPI.list(params); - if (response.success) { - setLogs(response.data.logs); - setPagination(prev => ({ ...prev, total: response.data.total })); - } - } catch (error) { - message.error('获取操作日志失败'); - } finally { - setLoading(false); - } - }; - - const fetchOptions = async () => { - try { - const [actionsRes, modulesRes] = await Promise.all([ - operationLogAPI.getActions(), - operationLogAPI.getModules() - ]); - if (actionsRes.success) setActions(actionsRes.data); - if (modulesRes.success) setModules(modulesRes.data); - } catch (error) { - console.error('获取选项失败:', error); - } - }; - - const handleFilterChange = (key, value) => { - setFilters(prev => ({ ...prev, [key]: value })); - setPagination(prev => ({ ...prev, current: 1 })); - }; - - const handleDateChange = (dates) => { - if (dates) { - setFilters(prev => ({ - ...prev, - startDate: dates[0].toISOString(), - endDate: dates[1].toISOString() - })); - } else { - setFilters(prev => ({ ...prev, startDate: undefined, endDate: undefined })); - } - setPagination(prev => ({ ...prev, current: 1 })); - }; - - const handleClear = async () => { - try { - const response = await operationLogAPI.clear({ days: 30 }); - if (response.success) { - message.success('已清理30天前的日志'); - fetchLogs(); - } - } catch (error) { - message.error('清理失败'); - } - }; - - const showDetail = (log) => { - setSelectedLog(log); - setDetailVisible(true); - }; - - const getActionColor = (action) => { - if (action.includes('删除')) return 'red'; - if (action.includes('创建')) return 'green'; - if (action.includes('修改')) return 'blue'; - if (action.includes('登录')) return 'purple'; - return 'default'; - }; - - const getModuleColor = (module) => { - const colors = { - user: 'blue', - role: 'green', - device: 'orange', - consumable: 'purple', - system: 'cyan' - }; - return colors[module] || 'default'; - }; - - const columns = [ - { - title: '操作时间', - dataIndex: 'operateTime', - key: 'operateTime', - width: 180, - sorter: (a, b) => new Date(b.operateTime) - new Date(a.operateTime), - render: (time) => time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '-' - }, - { - title: '操作人', - key: 'operator', - width: 150, - render: (_, record) => ( - - {record.realName || record.username} - @{record.username} - - ) - }, - { - title: '操作类型', - dataIndex: 'action', - key: 'action', - width: 120, - render: (action) => ( - {action || '-'} - ) - }, - { - title: '模块', - dataIndex: 'module', - key: 'module', - width: 100, - render: (module) => ( - - {module === 'user' ? '用户' : - module === 'role' ? '角色' : - module === 'device' ? '设备' : - module === 'consumable' ? '耗材' : - module === 'system' ? '系统' : module} - - ) - }, - { - title: '描述', - dataIndex: 'description', - key: 'description', - ellipsis: true - }, - { - title: '目标', - key: 'target', - width: 120, - render: (_, record) => record.targetName || record.targetId || '-' - }, - { - title: 'IP', - dataIndex: 'ip', - key: 'ip', - width: 130, - render: (ip) => ip || '-' - }, - { - title: '状态', - dataIndex: 'status', - key: 'status', - width: 80, - render: (status) => ( - - {status === 'success' ? '成功' : '失败'} - - ) - }, - { - title: '操作', - key: 'action', - width: 80, - render: (_, record) => ( - } - onClick={() => showDetail(record)} - /> - ) - } - ]; - - const pageHeaderStyle = { - marginBottom: '24px', - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center' - }; - - const titleStyle = { - fontSize: '20px', - fontWeight: '600', - margin: 0 - }; - - return ( - - - 操作日志 - - } onClick={fetchLogs}>刷新 - - 清理旧日志 - - - - - - - handleFilterChange('username', value)} - allowClear - /> - handleFilterChange('action', value)} - options={actions} - fieldNames={{ label: 'label', value: 'value' }} - /> - handleFilterChange('module', value)} - options={modules} - fieldNames={{ label: 'label', value: 'value' }} - /> - - - - - - `共 ${total} 条记录` - }} - onChange={(newPagination) => { - setPagination(prev => ({ ...prev, ...newPagination })); - }} - /> - - - setDetailVisible(false)} - > - {selectedLog && ( - - - {selectedLog.operateTime ? dayjs(selectedLog.operateTime).format('YYYY-MM-DD HH:mm:ss') : '-'} - - - {selectedLog.realName || selectedLog.username} (@{selectedLog.username}) - - - {selectedLog.action} - - - {selectedLog.module} - - {selectedLog.description || '-'} - - {selectedLog.targetName || selectedLog.targetId || '-'} - - {selectedLog.ip || '-'} - - - {selectedLog.status === 'success' ? '成功' : '失败'} - - - {selectedLog.errorMessage && ( - - {selectedLog.errorMessage} - - )} - - )} - {(selectedLog?.oldValue || selectedLog?.newValue) && ( - - 变更内容 - - {selectedLog.oldValue && ( - - - {JSON.stringify(JSON.parse(selectedLog.oldValue), null, 2)} - - - )} - {selectedLog.newValue && ( - - - {JSON.stringify(JSON.parse(selectedLog.newValue), null, 2)} - - - )} - - - )} - - - ); -}; - -export default OperationLogs;
- {JSON.stringify(JSON.parse(selectedLog.oldValue), null, 2)} -
- {JSON.stringify(JSON.parse(selectedLog.newValue), null, 2)} -