diff --git a/src/assets/styles/info-panel.css b/src/assets/styles/info-panel.css
index 752f7bb..79c3928 100644
--- a/src/assets/styles/info-panel.css
+++ b/src/assets/styles/info-panel.css
@@ -9,9 +9,9 @@
right: -400px; /* 初始隐藏在右侧 */
width: 400px;
height: calc(100vh - 60px);
- background: var(--color-bg-primary);
- border-left: 1px solid var(--color-border-primary);
- box-shadow: -4px 0 15px rgba(0, 0, 0, 0.1);
+ background: linear-gradient(135deg, #1e1e1e 0%, #2a2a2a 100%);
+ border-left: 1px solid rgba(255, 255, 255, 0.1);
+ box-shadow: -4px 0 15px rgba(0, 0, 0, 0.3);
z-index: 1000;
display: flex;
flex-direction: column;
@@ -28,8 +28,8 @@
align-items: center;
justify-content: space-between;
padding: 15px 20px;
- background: var(--color-bg-primary);
- border-bottom: 1px solid var(--color-border-primary);
+ background: #1e1e1e;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
min-height: 60px;
}
@@ -37,13 +37,13 @@
display: flex;
align-items: center;
gap: 10px;
- color: var(--color-text-primary);
+ color: #ffffff;
font-weight: 600;
font-size: 16px;
}
.panel-title i {
- color: var(--color-primary);
+ color: #2a5caa;
font-size: 18px;
}
@@ -59,15 +59,15 @@
gap: 6px;
padding: 4px 8px;
border-radius: 4px;
- background: var(--color-error-rgb-1);
- color: var(--color-text-error);
+ background: rgba(244, 67, 54, 0.1);
+ color: #f44336;
font-size: 12px;
font-weight: 500;
}
.status-indicator.connected {
- background: var(--color-success-rgb-1);
- color: var(--color-text-success);
+ background: rgba(76, 175, 80, 0.1);
+ color: #4caf50;
}
.status-indicator i {
@@ -77,7 +77,7 @@
.panel-close {
background: none;
border: none;
- color: var(--color-text-secondary);
+ color: #cccccc;
cursor: pointer;
padding: 6px;
border-radius: 4px;
@@ -85,8 +85,8 @@
}
.panel-close:hover {
- background: var(--color-white-rgb-1);
- color: var(--color-text-primary);
+ background: #2a2a2a;
+ color: #ffffff;
}
/* 面板内容区域 */
@@ -100,8 +100,8 @@
/* 选项卡导航 */
.panel-tabs {
display: flex;
- background: var(--color-bg-secondary);
- border-bottom: 1px solid var(--color-border-primary);
+ background: #2a2a2a;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.tab-btn {
@@ -113,7 +113,7 @@
padding: 12px 8px;
background: none;
border: none;
- color: var(--color-text-secondary);
+ color: #cccccc;
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
@@ -121,14 +121,14 @@
}
.tab-btn:hover {
- background: var(--color-white-rgb-05);
- color: var(--color-text-primary);
+ background: rgba(255, 255, 255, 0.05);
+ color: #ffffff;
}
.tab-btn.active {
- color: var(--color-primary);
- border-bottom-color: var(--color-primary);
- background: var(--color-bg-primary);
+ color: #2a5caa;
+ border-bottom-color: #2a5caa;
+ background: #1e1e1e;
}
.tab-btn i {
@@ -155,16 +155,16 @@
display: flex;
align-items: center;
justify-content: space-between;
- color: var(--color-text-primary);
+ color: #ffffff;
font-size: 14px;
font-weight: 600;
margin: 0 0 15px 0;
padding-bottom: 8px;
- border-bottom: 1px solid var(--color-border-secondary);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.section-title i {
- color: var(--color-primary);
+ color: #2a5caa;
margin-right: 8px;
}
@@ -175,12 +175,12 @@
justify-content: center;
gap: 10px;
padding: 40px 20px;
- color: var(--color-text-secondary);
+ color: #cccccc;
font-size: 14px;
}
.loading-state i {
- color: var(--color-primary);
+ color: #2a5caa;
font-size: 16px;
}
@@ -196,15 +196,15 @@
flex-direction: column;
gap: 12px;
padding: 16px;
- background: var(--color-white-rgb-05);
- border: 1px solid var(--color-border-secondary);
+ background: #2a2a2a;
+ border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
transition: all 0.2s ease;
}
.software-item-backend:hover {
- border-color: var(--color-border-primary);
- background: var(--color-white-rgb-1);
+ border-color: rgba(255, 255, 255, 0.3);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.software-info {
@@ -220,7 +220,7 @@
display: flex;
align-items: center;
justify-content: center;
- background: var(--color-primary-gradient);
+ background: linear-gradient(135deg, #2a5caa 0%, #3d7bd8 100%);
border-radius: 6px;
color: white;
font-size: 14px;
@@ -232,19 +232,19 @@
}
.software-name-backend {
- color: var(--color-text-primary);
+ color: #ffffff;
font-weight: 500;
font-size: 14px;
margin-bottom: 2px;
}
.software-status {
- color: var(--color-text-secondary);
+ color: #cccccc;
font-size: 12px;
}
.software-status.running {
- color: var(--color-text-success);
+ color: #4caf50;
}
.software-actions {
@@ -271,41 +271,63 @@
}
.action-btn.start {
- background: var(--color-success-gradient);
+ background: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
color: white;
}
.action-btn.start:hover:not(:disabled) {
- background: var(--color-success-gradient-hover);
+ background: linear-gradient(135deg, #66bb6a 0%, #81c784 100%);
transform: translateY(-1px);
- box-shadow: 0 4px 8px var(--color-success-rgb-3);
+ box-shadow: 0 4px 8px rgba(76, 175, 80, 0.3);
}
.action-btn.stop {
- background: var(--color-error-gradient);
+ background: linear-gradient(135deg, #f44336 0%, #ef5350 100%);
color: white;
}
.action-btn.stop:hover:not(:disabled) {
- background: var(--color-error-gradient-hover);
+ background: linear-gradient(135deg, #ef5350 0%, #e57373 100%);
transform: translateY(-1px);
- box-shadow: 0 4px 8px var(--color-error-rgb-3);
+ box-shadow: 0 4px 8px rgba(244, 67, 54, 0.3);
}
.action-btn.restart {
- background: var(--color-warning-gradient);
+ background: linear-gradient(135deg, #ff9800 0%, #ffb74d 100%);
color: white;
}
.action-btn.restart:hover:not(:disabled) {
- background: var(--color-warning-gradient-hover);
+ background: linear-gradient(135deg, #ffb74d 0%, #ffcc02 100%);
transform: translateY(-1px);
- box-shadow: 0 4px 8px var(--color-warning-rgb-3);
+ box-shadow: 0 4px 8px rgba(255, 152, 0, 0.3);
+}
+
+.action-btn.refresh {
+ background: linear-gradient(135deg, #2196f3 0%, #42a5f5 100%);
+ color: white;
+}
+
+.action-btn.refresh:hover:not(:disabled) {
+ background: linear-gradient(135deg, #42a5f5 0%, #64b5f6 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 8px rgba(33, 150, 243, 0.3);
+}
+
+.action-btn.clear {
+ background: linear-gradient(135deg, #f44336 0%, #ef5350 100%);
+ color: white;
+}
+
+.action-btn.clear:hover:not(:disabled) {
+ background: linear-gradient(135deg, #ef5350 0%, #e57373 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 8px rgba(244, 67, 54, 0.3);
}
.action-btn:disabled {
- background: var(--color-white-rgb-1);
- color: var(--color-text-disabled);
+ background: #1a1a1a;
+ color: #999999;
cursor: not-allowed;
opacity: 0.6;
box-shadow: none;
@@ -315,7 +337,7 @@
.refresh-btn {
background: none;
border: none;
- color: var(--color-text-secondary);
+ color: #cccccc;
cursor: pointer;
padding: 4px;
border-radius: 4px;
@@ -323,8 +345,8 @@
}
.refresh-btn:hover {
- color: var(--color-primary);
- background: var(--color-primary-rgb-1);
+ color: #2a5caa;
+ background: rgba(42, 92, 170, 0.1);
}
/* 日志面板样式 */
@@ -348,10 +370,10 @@
.filter-select, .filter-input {
padding: 6px 10px;
- border: 1px solid var(--color-border-secondary);
+ border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
- background: var(--color-white-rgb-05);
- color: var(--color-text-primary);
+ background: #2a2a2a;
+ color: #ffffff;
font-size: 12px;
}
@@ -365,20 +387,20 @@
.filter-select:focus, .filter-input:focus {
outline: none;
- border-color: var(--color-primary);
+ border-color: #2a5caa;
}
.logs-list {
max-height: 300px;
overflow-y: auto;
- border: 1px solid var(--color-border-secondary);
+ border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
- background: var(--color-white-rgb-05);
+ background: #2a2a2a;
}
.log-item {
padding: 10px 15px;
- border-bottom: 1px solid var(--color-border-secondary);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
transition: background 0.2s ease;
}
@@ -387,7 +409,7 @@
}
.log-item:hover {
- background: var(--color-white-rgb-1);
+ background: #1a1a1a;
}
.log-header {
@@ -398,18 +420,18 @@
}
.log-operation {
- color: var(--color-text-primary);
+ color: #ffffff;
font-weight: 500;
font-size: 13px;
}
.log-time {
- color: var(--color-text-secondary);
+ color: #cccccc;
font-size: 11px;
}
.log-details {
- color: var(--color-text-secondary);
+ color: #cccccc;
font-size: 12px;
line-height: 1.4;
}
@@ -420,11 +442,11 @@
gap: 10px;
margin-top: 4px;
font-size: 11px;
- color: var(--color-text-tertiary);
+ color: #888888;
}
.log-user {
- color: var(--color-primary);
+ color: #2a5caa;
}
.log-status {
@@ -435,13 +457,13 @@
}
.log-status.success {
- background: var(--color-success-rgb-1);
- color: var(--color-text-success);
+ background: rgba(76, 175, 80, 0.1);
+ color: #4caf50;
}
.log-status.failed {
- background: var(--color-error-rgb-1);
- color: var(--color-text-error);
+ background: rgba(244, 67, 54, 0.1);
+ color: #f44336;
}
.logs-pagination {
@@ -451,34 +473,34 @@
gap: 15px;
margin-top: 15px;
padding-top: 15px;
- border-top: 1px solid var(--color-border-secondary);
+ border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.page-btn {
padding: 6px 12px;
- border: 1px solid var(--color-border-secondary);
+ border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
- background: var(--color-white-rgb-05);
- color: var(--color-text-secondary);
+ background: #2a2a2a;
+ color: #cccccc;
cursor: pointer;
font-size: 12px;
transition: all 0.2s ease;
}
.page-btn:hover {
- border-color: var(--color-primary);
- color: var(--color-primary);
+ border-color: #2a5caa;
+ color: #2a5caa;
}
.page-btn:disabled {
- background: var(--color-white-rgb-1);
- color: var(--color-text-disabled);
+ background: #1a1a1a;
+ color: #999999;
cursor: not-allowed;
- border-color: var(--color-border-secondary);
+ border-color: rgba(255, 255, 255, 0.2);
}
.page-info {
- color: var(--color-text-secondary);
+ color: #cccccc;
font-size: 12px;
font-weight: 500;
}
@@ -493,8 +515,8 @@
.stat-card {
padding: 15px;
- background: var(--color-white-rgb-05);
- border: 1px solid var(--color-border-secondary);
+ background: #2a2a2a;
+ border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
text-align: center;
}
@@ -503,23 +525,23 @@
display: block;
font-size: 24px;
font-weight: bold;
- color: var(--color-primary);
+ color: #2a5caa;
margin-bottom: 5px;
}
.stat-label {
- color: var(--color-text-secondary);
+ color: #cccccc;
font-size: 12px;
font-weight: 500;
}
.operation-types {
padding-top: 20px;
- border-top: 1px solid var(--color-border-secondary);
+ border-top: 1px solid rgba(255, 255, 255, 0.2);
}
.operation-types h5 {
- color: var(--color-text-primary);
+ color: #ffffff;
font-size: 13px;
font-weight: 600;
margin: 0 0 10px 0;
@@ -533,10 +555,10 @@
.type-tag {
padding: 4px 8px;
- background: var(--color-white-rgb-05);
- border: 1px solid var(--color-border-secondary);
+ background: #2a2a2a;
+ border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
- color: var(--color-text-secondary);
+ color: #cccccc;
font-size: 11px;
font-weight: 500;
}
diff --git a/src/components/layout/InfoManagementPanel.vue b/src/components/layout/InfoManagementPanel.vue
index a91474b..ba1a09b 100644
--- a/src/components/layout/InfoManagementPanel.vue
+++ b/src/components/layout/InfoManagementPanel.vue
@@ -59,25 +59,25 @@
>
-
+
-
{{ software.name }}
-
- {{ software.isRunning ? '运行中' : '已停止' }}
+
{{ software.name || software.id }}
+
+ {{ getStatusText(software.status) }}
-
{{ log.details }}
+
{{ log.details }}
- {{ log.user }}
- {{ log.status }}
+ {{ log.user_id }}
+ {{ getLogStatusText(log.status) }}
@@ -165,20 +165,20 @@
- {{ stats.totalOperations }}
- 总操作次数
+ {{ stats.totalOperations || 0 }}
+ 总日志数
- {{ stats.successfulOperations }}
- 成功操作
+ {{ stats.successfulOperations || 0 }}
+ 用户操作
- {{ stats.failedOperations }}
- 失败操作
+ {{ stats.failedOperations || 0 }}
+ 错误日志
- {{ stats.activeConnections }}
- 活动连接
+ {{ stats.activeConnections || 0 }}
+ 系统操作
@@ -201,7 +201,8 @@
diff --git a/src/config/cad.js b/src/config/cad.js
index 6f81ec0..59675fc 100644
--- a/src/config/cad.js
+++ b/src/config/cad.js
@@ -6,6 +6,17 @@ const API_CONFIG = {
BASE_URL: import.meta.env.MODE === 'production' ? 'https://api.miany.com' : 'http://localhost',
TIMEOUT: 30000, // 30秒超时
+ // WebSocket配置
+ WEBSOCKET: {
+ BASE_URL: 'ws://localhost:8000',
+ ENDPOINT: '/api/v1/ws/connect',
+ CLIENT_ID: 'web_client',
+ USER_ID: 'admin',
+ RECONNECT_ATTEMPTS: 5,
+ RECONNECT_DELAY: 1000, // 1秒
+ HEARTBEAT_INTERVAL: 30000 // 30秒
+ },
+
// 通用API端点
COMMON_ENDPOINTS: {
health: '/health',
@@ -240,6 +251,11 @@ export const getGeometryOptimizationDefaults = () => {
return API_CONFIG.GEOMETRY_OPTIMIZATION
}
+// 获取WebSocket配置
+export const getWebSocketConfig = () => {
+ return API_CONFIG.WEBSOCKET
+}
+
// HTTP请求头配置
export const getRequestHeaders = () => {
return {
diff --git a/src/main.js b/src/main.js
index b2e2d2f..496bf4d 100644
--- a/src/main.js
+++ b/src/main.js
@@ -6,6 +6,7 @@ import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
import { useAuthStore } from './stores/auth'
+import websocketService from './services/websocketService'
const app = createApp(App)
@@ -17,4 +18,7 @@ app.use(ElementPlus)
const authStore = useAuthStore()
authStore.initAuth()
+// 启动WebSocket连接
+websocketService.connect()
+
app.mount('#app')
diff --git a/src/services/websocketService.js b/src/services/websocketService.js
new file mode 100644
index 0000000..28dfca0
--- /dev/null
+++ b/src/services/websocketService.js
@@ -0,0 +1,392 @@
+// WebSocket服务类 - 统一管理后台WebSocket连接
+// 基于参考项目接口格式实现
+
+import { getWebSocketConfig } from '@/config/cad'
+
+class WebSocketService {
+ constructor() {
+ this.config = getWebSocketConfig()
+ this.ws = null
+ this.connectionState = 'DISCONNECTED' // CONNECTING, CONNECTED, DISCONNECTED, ERROR
+ this.reconnectAttempts = 0
+ this.heartbeatTimer = null
+
+ // 状态数据
+ this.softwareList = []
+ this.logs = []
+ this.logStats = null
+ this.operationTypes = []
+ this.connectedUsers = []
+
+ // 事件监听器
+ this.listeners = {
+ onOpen: [],
+ onMessage: [],
+ onClose: [],
+ onError: [],
+ onStateChange: [],
+ onSoftwareUpdate: [],
+ onLogUpdate: [],
+ onStatsUpdate: [],
+ onOperationTypesUpdate: []
+ }
+
+ console.log('WebSocketService 初始化完成')
+ }
+
+ // 获取WebSocket连接URL
+ getConnectionUrl() {
+ const { BASE_URL, ENDPOINT, CLIENT_ID, USER_ID } = this.config
+ return `${BASE_URL}${ENDPOINT}?client_id=${CLIENT_ID}&user_id=${USER_ID}`
+ }
+
+ // 连接WebSocket
+ connect() {
+ if (this.connectionState === 'CONNECTING' || this.connectionState === 'CONNECTED') {
+ return
+ }
+
+ const url = this.getConnectionUrl()
+ console.log('开始连接统一管理后台:', url)
+ this.setState('CONNECTING')
+ this.ws = new WebSocket(url)
+
+ this.ws.onopen = (event) => {
+ console.log('统一管理后台连接成功')
+ this.setState('CONNECTED')
+ this.reconnectAttempts = 0
+ this.startHeartbeat()
+ this.emit('onOpen', event)
+
+ // 连接成功后立即获取初始数据
+ setTimeout(() => {
+ this.getSoftwareList()
+ this.getLogStats()
+ this.getOperationTypes()
+ }, 500)
+ }
+
+ this.ws.onmessage = (event) => {
+ try {
+ const message = JSON.parse(event.data)
+ console.log('收到管理后台消息:', message)
+ this.handleMessage(message)
+ this.emit('onMessage', message)
+ } catch (error) {
+ console.error('解析管理后台消息失败:', error, event.data)
+ }
+ }
+
+ this.ws.onclose = (event) => {
+ console.log('统一管理后台连接关闭:', event)
+ this.setState('DISCONNECTED')
+ this.stopHeartbeat()
+ this.emit('onClose', event)
+
+ // 自动重连
+ if (!event.wasClean && this.reconnectAttempts < this.config.RECONNECT_ATTEMPTS) {
+ const delay = this.config.RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts)
+ console.log(`统一管理后台将在 ${delay}ms 后重连,第 ${this.reconnectAttempts + 1} 次尝试`)
+ setTimeout(() => {
+ this.reconnectAttempts++
+ this.connect()
+ }, delay)
+ }
+ }
+
+ this.ws.onerror = (event) => {
+ console.error('统一管理后台连接错误:', event)
+ this.setState('ERROR')
+ this.emit('onError', event)
+ }
+ }
+
+ // 断开连接
+ disconnect() {
+ console.log('主动断开统一管理后台连接')
+ if (this.ws) {
+ this.ws.close(1000, 'Client disconnect')
+ this.ws = null
+ }
+ this.stopHeartbeat()
+ this.setState('DISCONNECTED')
+ }
+
+ // 发送消息
+ send(message) {
+ if (this.connectionState === 'CONNECTED' && this.ws) {
+ const jsonMessage = JSON.stringify(message)
+ console.log('发送管理后台消息:', message)
+ this.ws.send(jsonMessage)
+ return true
+ } else {
+ console.warn('统一管理后台未连接,消息发送失败:', message)
+ return false
+ }
+ }
+
+ // 处理收到的消息
+ handleMessage(message) {
+ switch (message.type) {
+ case 'info':
+ this.handleInfoMessage(message)
+ break
+ case 'heartbeat':
+ console.log('收到心跳响应')
+ break
+ case 'software_list_update':
+ this.handleSoftwareListUpdate(message)
+ break
+ case 'log_recorded':
+ console.log('操作日志已记录:', message.data)
+ break
+ case 'error':
+ console.error('管理后台错误:', message.message)
+ break
+ default:
+ console.log('未处理的消息类型:', message.type, message)
+ }
+ }
+
+ // 处理info类型消息
+ handleInfoMessage(message) {
+ if (message.data) {
+ if (message.data.software_list) {
+ this.softwareList = message.data.software_list
+ this.emit('onSoftwareUpdate', this.softwareList)
+ }
+
+ if (message.data.logs) {
+ this.logs = message.data.logs
+ this.emit('onLogUpdate', this.logs)
+ }
+
+ if (message.data.stats) {
+ this.logStats = message.data.stats
+ this.emit('onStatsUpdate', this.logStats)
+ }
+
+ if (message.data.operations) {
+ this.operationTypes = message.data.operations
+ this.emit('onOperationTypesUpdate', this.operationTypes)
+ }
+
+ if (message.data.connected_users) {
+ this.connectedUsers = message.data.connected_users
+ }
+ }
+ }
+
+ // 处理软件列表更新
+ handleSoftwareListUpdate(message) {
+ if (message.data && message.data.software_list) {
+ this.softwareList = message.data.software_list
+ console.log('软件列表已更新:', this.softwareList)
+ this.emit('onSoftwareUpdate', this.softwareList)
+ }
+ }
+
+ // API方法 - 心跳检测
+ ping() {
+ return this.send({ type: 'ping' })
+ }
+
+ // API方法 - 获取服务状态
+ getStatus() {
+ return this.send({ type: 'get_status' })
+ }
+
+ // API方法 - 获取软件列表
+ getSoftwareList() {
+ return this.send({ type: 'get_software_list' })
+ }
+
+ // API方法 - 启动软件
+ startSoftware(softwareId) {
+ if (!softwareId || typeof softwareId !== 'string' || softwareId.length === 0) {
+ console.error('启动软件失败: 无效的软件ID', softwareId)
+ return false
+ }
+
+ console.log('启动软件:', softwareId)
+ return this.send({
+ type: 'start_software',
+ software_id: softwareId
+ })
+ }
+
+ // API方法 - 停止软件
+ stopSoftware(softwareId) {
+ if (!softwareId || typeof softwareId !== 'string' || softwareId.length === 0) {
+ console.error('停止软件失败: 无效的软件ID', softwareId)
+ return false
+ }
+
+ console.log('停止软件:', softwareId)
+ return this.send({
+ type: 'stop_software',
+ software_id: softwareId
+ })
+ }
+
+ // API方法 - 重启软件
+ restartSoftware(softwareId) {
+ if (!softwareId || typeof softwareId !== 'string' || softwareId.length === 0) {
+ console.error('重启软件失败: 无效的软件ID', softwareId)
+ return false
+ }
+
+ console.log('重启软件:', softwareId)
+ return this.send({
+ type: 'restart_software',
+ software_id: softwareId
+ })
+ }
+
+ // API方法 - 记录操作日志
+ logOperation(operation, details = '', options = {}) {
+ if (!operation || typeof operation !== 'string' || operation.length === 0) {
+ console.error('记录日志失败: 操作名称不能为空')
+ return false
+ }
+
+ const logData = {
+ type: 'log_operation',
+ operation: operation.substring(0, 100), // 限制长度
+ details: details ? details.substring(0, 1000) : '', // 限制长度
+ action_type: options.action_type || 'execute',
+ target_object: options.target_object || '',
+ status: options.status || 'success',
+ duration: options.duration || 0,
+ operation_category: options.operation_category || '软件控制'
+ }
+
+ console.log('记录操作日志:', logData)
+ return this.send(logData)
+ }
+
+ // API方法 - 查询操作日志
+ queryLogs(filters = {}) {
+ const queryData = {
+ type: 'query_logs',
+ limit: filters.limit || 100,
+ offset: filters.offset || 0
+ }
+
+ // 添加可选的筛选条件
+ if (filters.log_type) queryData.log_type = filters.log_type
+ if (filters.operation) queryData.operation = filters.operation
+ if (filters.user_id_filter) queryData.user_id_filter = filters.user_id_filter
+ if (filters.level) queryData.level = filters.level
+ if (filters.start_time) queryData.start_time = filters.start_time
+ if (filters.end_time) queryData.end_time = filters.end_time
+
+ console.log('查询操作日志:', queryData)
+ return this.send(queryData)
+ }
+
+ // API方法 - 根据ID获取日志
+ getLogById(logId) {
+ if (!logId || typeof logId !== 'string' || logId.length === 0) {
+ console.error('获取日志失败: 无效的日志ID', logId)
+ return false
+ }
+
+ return this.send({
+ type: 'get_log_by_id',
+ log_id: logId
+ })
+ }
+
+ // API方法 - 获取日志统计信息
+ getLogStats() {
+ console.log('获取日志统计信息')
+ return this.send({ type: 'get_log_stats' })
+ }
+
+ // API方法 - 清理过期日志
+ cleanupLogs() {
+ console.log('清理过期日志')
+ return this.send({ type: 'cleanup_logs' })
+ }
+
+ // API方法 - 获取操作类型列表
+ getOperationTypes() {
+ console.log('获取操作类型列表')
+ return this.send({ type: 'get_operation_types' })
+ }
+
+ // 心跳检测
+ startHeartbeat() {
+ this.heartbeatTimer = setInterval(() => {
+ if (this.connectionState === 'CONNECTED') {
+ this.ping()
+ }
+ }, this.config.HEARTBEAT_INTERVAL)
+ }
+
+ stopHeartbeat() {
+ if (this.heartbeatTimer) {
+ clearInterval(this.heartbeatTimer)
+ this.heartbeatTimer = null
+ }
+ }
+
+ // 状态管理
+ setState(newState) {
+ if (this.connectionState !== newState) {
+ const oldState = this.connectionState
+ this.connectionState = newState
+ console.log(`统一管理后台状态变化: ${oldState} -> ${newState}`)
+ this.emit('onStateChange', { oldState, newState })
+ }
+ }
+
+ // 事件系统
+ on(event, callback) {
+ if (this.listeners[event]) {
+ this.listeners[event].push(callback)
+ } else {
+ console.warn('未知事件类型:', event)
+ }
+ }
+
+ off(event, callback) {
+ if (this.listeners[event]) {
+ const index = this.listeners[event].indexOf(callback)
+ if (index > -1) {
+ this.listeners[event].splice(index, 1)
+ }
+ }
+ }
+
+ emit(event, data) {
+ if (this.listeners[event]) {
+ this.listeners[event].forEach(callback => {
+ try {
+ callback(data)
+ } catch (error) {
+ console.error('事件回调执行失败:', event, error)
+ }
+ })
+ }
+ }
+
+ // 获取当前状态
+ getState() {
+ return {
+ connectionState: this.connectionState,
+ softwareList: this.softwareList,
+ logs: this.logs,
+ logStats: this.logStats,
+ operationTypes: this.operationTypes,
+ connectedUsers: this.connectedUsers
+ }
+ }
+}
+
+// 导出单例实例
+export const websocketService = new WebSocketService()
+
+// 默认导出
+export default websocketService
\ No newline at end of file