feat: 完成WebSocket日志系统接入和信息面板状态同步
- 在API客户端添加WebSocket日志记录钩子,所有CAD操作自动记录到后台 - 修复信息面板软件状态字段名(is_running vs status)和分页逻辑错误 - CAD连接成功时自动同步更新信息面板软件状态显示 - WebSocket连接时主动获取历史日志,实现完整的日志显示功能 - 清理调试输出,优化用户体验 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
4d02c503ef
commit
a27e9aaec1
20
CLAUDE.md
20
CLAUDE.md
@ -142,6 +142,8 @@ const handleOperation = async () => {
|
||||
- ✅ Creo专属页面框架(模型分析、导出工具)
|
||||
- ✅ 通用格式转换页面框架
|
||||
- ✅ 智能薄壳化分析接口(startShellAnalysis)
|
||||
- ✅ WebSocket日志系统完整接入
|
||||
- ✅ 信息面板实时日志显示和软件状态同步
|
||||
|
||||
## 页面添加标准流程
|
||||
|
||||
@ -153,13 +155,29 @@ const handleOperation = async () => {
|
||||
- 添加页面切换处理方法
|
||||
4. **组件间通信** - 使用emit事件,不用router.push
|
||||
|
||||
## 日志系统实现总结
|
||||
|
||||
### ✅ 完成内容
|
||||
1. **API钩子集成** - 所有CAD操作自动通过WebSocket记录日志到后台
|
||||
2. **实时状态同步** - CAD连接成功后自动更新信息面板软件状态
|
||||
3. **字段名修复** - 修复了后台返回`is_running`字段与前端`status`字段不匹配的问题
|
||||
4. **分页逻辑修复** - 修复了翻页时重复调用后台接口导致的分页错乱
|
||||
5. **日志实时显示** - 信息面板实时接收并按时间排序显示操作日志
|
||||
|
||||
### 🔧 关键修改
|
||||
- **apiClient.js**: 添加WebSocket日志记录钩子和CAD连接成功时的状态同步
|
||||
- **InfoManagementPanel.vue**: 修复软件状态字段名和分页逻辑
|
||||
- **websocketService.js**: 连接时主动获取历史日志,清理调试输出
|
||||
|
||||
### 💡 数据流
|
||||
`CAD操作` → `API调用` → `后处理钩子` → `WebSocket记录` → `后台存储` → `实时推送` → `信息面板显示`
|
||||
|
||||
## 待开发功能
|
||||
|
||||
- Creo模型分析、导出工具具体功能实现
|
||||
- 通用格式转换具体功能实现
|
||||
- Revit和PDMS集成
|
||||
- 3D模型查看器
|
||||
- 实时日志系统
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -63,21 +63,21 @@
|
||||
</div>
|
||||
<div class="software-details">
|
||||
<div class="software-name-backend">{{ software.name || software.id }}</div>
|
||||
<div class="software-status" :class="{ running: isRunning(software.status) }">
|
||||
{{ getStatusText(software.status) }}
|
||||
<div class="software-status" :class="{ running: software.is_running }">
|
||||
{{ software.is_running ? '运行中' : '已停止' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="software-actions">
|
||||
<button
|
||||
v-if="!isRunning(software.status)"
|
||||
v-if="!software.is_running"
|
||||
class="action-btn start"
|
||||
@click="startSoftware(software.id)"
|
||||
>
|
||||
<i class="fas fa-play"></i>启动
|
||||
</button>
|
||||
<button
|
||||
v-if="isRunning(software.status)"
|
||||
v-if="software.is_running"
|
||||
class="action-btn stop"
|
||||
@click="stopSoftware(software.id)"
|
||||
>
|
||||
@ -86,7 +86,7 @@
|
||||
<button
|
||||
class="action-btn restart"
|
||||
@click="restartSoftware(software.id)"
|
||||
:disabled="!isRunning(software.status)"
|
||||
:disabled="!software.is_running"
|
||||
>
|
||||
<i class="fas fa-redo"></i>重启
|
||||
</button>
|
||||
@ -134,13 +134,20 @@
|
||||
class="log-item"
|
||||
>
|
||||
<div class="log-header">
|
||||
<div class="log-operation">{{ log.operation }}</div>
|
||||
<div class="log-operation">
|
||||
<i class="fas fa-cogs" v-if="log.operation_category === 'CAD操作'"></i>
|
||||
<i class="fas fa-desktop" v-else-if="log.operation_category === '软件控制'"></i>
|
||||
<i class="fas fa-gear" v-else></i>
|
||||
{{ log.operation }}
|
||||
</div>
|
||||
<div class="log-time">{{ formatTime(log.timestamp) }}</div>
|
||||
</div>
|
||||
<div class="log-details" v-if="log.details">{{ log.details }}</div>
|
||||
<div class="log-meta">
|
||||
<span class="log-category" v-if="log.operation_category">{{ log.operation_category }}</span>
|
||||
<span class="log-user" v-if="log.user_id">{{ log.user_id }}</span>
|
||||
<span class="log-status" :class="log.status">{{ getLogStatusText(log.status) }}</span>
|
||||
<span class="log-duration" v-if="log.duration">{{ log.duration }}ms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -236,8 +243,8 @@ const operationFilter = ref('')
|
||||
const currentPage = ref(1)
|
||||
const logsPerPage = 10
|
||||
|
||||
// 过滤后的日志
|
||||
const filteredLogs = computed(() => {
|
||||
// 过滤后的完整日志
|
||||
const allFilteredLogs = computed(() => {
|
||||
let filtered = logs.value
|
||||
|
||||
if (logTypeFilter.value) {
|
||||
@ -252,14 +259,19 @@ const filteredLogs = computed(() => {
|
||||
)
|
||||
}
|
||||
|
||||
return filtered
|
||||
})
|
||||
|
||||
// 当前页显示的日志
|
||||
const filteredLogs = computed(() => {
|
||||
const start = (currentPage.value - 1) * logsPerPage
|
||||
const end = start + logsPerPage
|
||||
return filtered.slice(start, end)
|
||||
return allFilteredLogs.value.slice(start, end)
|
||||
})
|
||||
|
||||
// 总页数
|
||||
const totalPages = computed(() => {
|
||||
return Math.ceil(logs.value.length / logsPerPage)
|
||||
return Math.ceil(allFilteredLogs.value.length / logsPerPage)
|
||||
})
|
||||
|
||||
// WebSocket事件监听器管理
|
||||
@ -283,9 +295,10 @@ const initListeners = () => {
|
||||
// 监听日志更新
|
||||
const onLogUpdate = (logData) => {
|
||||
if (Array.isArray(logData)) {
|
||||
logs.value = logData
|
||||
// 新日志添加到顶部,保持最新日志在前
|
||||
logs.value = [...logData].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
|
||||
} else if (logData.logs) {
|
||||
logs.value = logData.logs
|
||||
logs.value = [...logData.logs].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
|
||||
}
|
||||
}
|
||||
websocketService.on('onLogUpdate', onLogUpdate)
|
||||
@ -433,14 +446,12 @@ const formatTime = (timestamp) => {
|
||||
const prevPage = () => {
|
||||
if (currentPage.value > 1) {
|
||||
currentPage.value--
|
||||
refreshLogs()
|
||||
}
|
||||
}
|
||||
|
||||
const nextPage = () => {
|
||||
if (currentPage.value < totalPages.value) {
|
||||
currentPage.value++
|
||||
refreshLogs()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
// 包含拦截器、日志记录和后处理能力
|
||||
|
||||
import { getDefaultRequestConfig, getNotificationConfig } from '@/config/cad'
|
||||
import cadConfig from '@/config/cad'
|
||||
import { ElNotification, ElLoading } from 'element-plus'
|
||||
import websocketService from './websocketService'
|
||||
|
||||
// 日志记录系统
|
||||
class ApiLogger {
|
||||
@ -59,6 +61,7 @@ class ApiClient {
|
||||
|
||||
// 设置通知处理器
|
||||
setupNotificationHandler() {
|
||||
// 通知处理器
|
||||
PostProcessManager.addHandler(async (response, context) => {
|
||||
// 默认显示通知,可通过配置关闭
|
||||
if (context.showNotification !== false && context.operationContext) {
|
||||
@ -84,6 +87,35 @@ class ApiClient {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// WebSocket日志记录处理器
|
||||
PostProcessManager.addHandler(async (response, context) => {
|
||||
if (context.operationContext) {
|
||||
const { software, operation } = context.operationContext
|
||||
const status = response.ok ? 'success' : 'failed'
|
||||
const details = response.ok ?
|
||||
`${software} ${operation}执行成功` :
|
||||
`${software} ${operation}执行失败: ${response.statusText || '未知错误'}`
|
||||
|
||||
// 发送到WebSocket后台记录
|
||||
websocketService.logOperation(
|
||||
`${software} - ${operation}`,
|
||||
details,
|
||||
{
|
||||
action_type: 'execute',
|
||||
target_object: context.url,
|
||||
status: status,
|
||||
duration: context.duration || 0,
|
||||
operation_category: 'CAD操作'
|
||||
}
|
||||
)
|
||||
|
||||
// CAD连接成功时同步更新信息面板软件状态
|
||||
if (response.ok && operation === '连接测试') {
|
||||
websocketService.getSoftwareList()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 核心请求方法
|
||||
|
||||
@ -31,7 +31,6 @@ class WebSocketService {
|
||||
onOperationTypesUpdate: []
|
||||
}
|
||||
|
||||
console.log('WebSocketService 初始化完成')
|
||||
}
|
||||
|
||||
// 获取WebSocket连接URL
|
||||
@ -47,12 +46,10 @@ class WebSocketService {
|
||||
}
|
||||
|
||||
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()
|
||||
@ -63,13 +60,13 @@ class WebSocketService {
|
||||
this.getSoftwareList()
|
||||
this.getLogStats()
|
||||
this.getOperationTypes()
|
||||
this.queryLogs()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data)
|
||||
console.log('收到管理后台消息:', message)
|
||||
this.handleMessage(message)
|
||||
this.emit('onMessage', message)
|
||||
} catch (error) {
|
||||
@ -78,7 +75,6 @@ class WebSocketService {
|
||||
}
|
||||
|
||||
this.ws.onclose = (event) => {
|
||||
console.log('统一管理后台连接关闭:', event)
|
||||
this.setState('DISCONNECTED')
|
||||
this.stopHeartbeat()
|
||||
this.emit('onClose', event)
|
||||
@ -86,7 +82,6 @@ class WebSocketService {
|
||||
// 自动重连
|
||||
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()
|
||||
@ -95,7 +90,6 @@ class WebSocketService {
|
||||
}
|
||||
|
||||
this.ws.onerror = (event) => {
|
||||
console.error('统一管理后台连接错误:', event)
|
||||
this.setState('ERROR')
|
||||
this.emit('onError', event)
|
||||
}
|
||||
@ -103,7 +97,6 @@ class WebSocketService {
|
||||
|
||||
// 断开连接
|
||||
disconnect() {
|
||||
console.log('主动断开统一管理后台连接')
|
||||
if (this.ws) {
|
||||
this.ws.close(1000, 'Client disconnect')
|
||||
this.ws = null
|
||||
@ -116,11 +109,9 @@ class WebSocketService {
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -132,13 +123,17 @@ class WebSocketService {
|
||||
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 'software_started':
|
||||
case 'software_stopped':
|
||||
case 'software_restarted':
|
||||
// 软件状态变更后自动刷新软件列表
|
||||
this.getSoftwareList()
|
||||
break
|
||||
case 'error':
|
||||
console.error('管理后台错误:', message.message)
|
||||
@ -181,7 +176,6 @@ class WebSocketService {
|
||||
handleSoftwareListUpdate(message) {
|
||||
if (message.data && message.data.software_list) {
|
||||
this.softwareList = message.data.software_list
|
||||
console.log('软件列表已更新:', this.softwareList)
|
||||
this.emit('onSoftwareUpdate', this.softwareList)
|
||||
}
|
||||
}
|
||||
@ -208,7 +202,6 @@ class WebSocketService {
|
||||
return false
|
||||
}
|
||||
|
||||
console.log('启动软件:', softwareId)
|
||||
return this.send({
|
||||
type: 'start_software',
|
||||
software_id: softwareId
|
||||
@ -222,7 +215,6 @@ class WebSocketService {
|
||||
return false
|
||||
}
|
||||
|
||||
console.log('停止软件:', softwareId)
|
||||
return this.send({
|
||||
type: 'stop_software',
|
||||
software_id: softwareId
|
||||
@ -236,7 +228,6 @@ class WebSocketService {
|
||||
return false
|
||||
}
|
||||
|
||||
console.log('重启软件:', softwareId)
|
||||
return this.send({
|
||||
type: 'restart_software',
|
||||
software_id: softwareId
|
||||
@ -261,7 +252,6 @@ class WebSocketService {
|
||||
operation_category: options.operation_category || '软件控制'
|
||||
}
|
||||
|
||||
console.log('记录操作日志:', logData)
|
||||
return this.send(logData)
|
||||
}
|
||||
|
||||
@ -281,7 +271,6 @@ class WebSocketService {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -300,19 +289,16 @@ class WebSocketService {
|
||||
|
||||
// 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' })
|
||||
}
|
||||
|
||||
@ -337,7 +323,6 @@ class WebSocketService {
|
||||
if (this.connectionState !== newState) {
|
||||
const oldState = this.connectionState
|
||||
this.connectionState = newState
|
||||
console.log(`统一管理后台状态变化: ${oldState} -> ${newState}`)
|
||||
this.emit('onStateChange', { oldState, newState })
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user