diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..8b0f18b --- /dev/null +++ b/.env.development @@ -0,0 +1,26 @@ +# 环境变量 (命名必须以 VITE_ 开头) +# 接口前缀 +VITE_API_PREFIX = '/dev-api' + +# 接口地址 +# VITE_API_BASE_URL = 'http://pms.dtyx.net:9158/' +# VITE_API_BASE_URL = 'http://localhost:8888/' +VITE_API_BASE_URL = 'http://10.18.34.163:8888/' +# VITE_API_BASE_URL = 'http://10.18.34.213:8888/' + +# 接口地址 (WebSocket) +# VITE_API_WS_URL = 'ws://localhost:8000' +VITE_API_WS_URL = 'ws://10.18.34.163:8000' +# VITE_API_WS_URL = 'ws://10.18.34.213:8000' + +# 地址前缀 +VITE_BASE = '/' + +# 是否开启开发者工具 +VITE_OPEN_DEVTOOLS = false + +# 应用配置面板 +VITE_APP_SETTING = true + +# 客户端ID +VITE_CLIENT_ID = 'ef51c9a3e9046c4f2ea45142c8a8344a' \ No newline at end of file diff --git a/.env.production b/.env.production index 8700360..cbbbfbc 100644 --- a/.env.production +++ b/.env.production @@ -14,4 +14,4 @@ VITE_BASE = '/' VITE_APP_SETTING = true # 客户端ID -VITE_CLIENT_ID = 'ef51c9a3e9046c4f2ea45142c8a8344a' \ No newline at end of file +VITE_CLIENT_ID = 'ef51c9a3e9046c4f2ea45142c8a8344a' diff --git a/public/favicon.ico b/public/favicon.ico index f3c48de..1fc12ce 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/src/layout/components/HeaderRightBar/index.vue b/src/layout/components/HeaderRightBar/index.vue index d1b57ae..64719de 100644 --- a/src/layout/components/HeaderRightBar/index.vue +++ b/src/layout/components/HeaderRightBar/index.vue @@ -19,8 +19,13 @@ :content-style="{ marginTop: '-5px', padding: 0, border: 'none' }" :arrow-style="{ width: 0, height: 0 }" > - - + + @@ -97,10 +102,148 @@ onBeforeUnmount(() => { socket.close() socket = null } + + // 清理标题闪烁 + if (titleFlashInterval) { + clearInterval(titleFlashInterval) + titleFlashInterval = null + } }) const unreadMessageCount = ref(0) +// 语音提示功能 +const playNotificationSound = () => { + try { + // 创建音频上下文 + const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)() + const oscillator = audioContext.createOscillator() + const gainNode = audioContext.createGain() + + oscillator.connect(gainNode) + gainNode.connect(audioContext.destination) + + // 设置音频参数 + oscillator.frequency.setValueAtTime(800, audioContext.currentTime) // 800Hz + oscillator.type = 'sine' + + gainNode.gain.setValueAtTime(0.3, audioContext.currentTime) + gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5) + + // 播放音频 + oscillator.start(audioContext.currentTime) + oscillator.stop(audioContext.currentTime + 0.5) + + console.log('播放语音提示') + } catch (error) { + console.error('播放语音提示失败:', error) + } +} + +// 页面标题闪烁功能 +let titleFlashInterval: NodeJS.Timeout | null = null +const flashPageTitle = () => { + const originalTitle = document.title + let flashCount = 0 + const maxFlashes = 6 // 闪烁3次(开-关-开-关-开-关) + + // 清除之前的闪烁 + if (titleFlashInterval) { + clearInterval(titleFlashInterval) + } + + titleFlashInterval = setInterval(() => { + if (flashCount >= maxFlashes) { + document.title = originalTitle + if (titleFlashInterval) { + clearInterval(titleFlashInterval) + titleFlashInterval = null + } + return + } + + document.title = flashCount % 2 === 0 ? '🔔 新的采购申请' : originalTitle + flashCount++ + }, 500) +} + +// 暴露测试函数到全局,方便在控制台测试 +if (typeof window !== 'undefined') { + (window as any).testNotification = { + playSound: playNotificationSound, + flashTitle: flashPageTitle, + showNotification: () => { + Notification.info({ + title: '测试通知', + content: '这是一个测试通知,用于验证通知功能是否正常工作。', + duration: 5000, + closable: true, + position: 'topRight' + }) + unreadMessageCount.value++ + }, + testAll: () => { + playNotificationSound() + flashPageTitle() + Notification.info({ + title: '测试通知', + content: '这是一个测试通知,用于验证通知功能是否正常工作。', + duration: 5000, + closable: true, + position: 'topRight' + }) + unreadMessageCount.value++ + }, + // 添加调试函数 + debugWebSocket: () => { + console.log('=== WebSocket 调试信息 ===') + console.log('Socket对象:', socket) + console.log('Socket状态:', socket ? socket.readyState : '未连接') + console.log('Token:', getToken()) + console.log('环境变量:', import.meta.env.VITE_API_WS_URL) + console.log('未读消息计数:', unreadMessageCount.value) + console.log('用户Token:', userStore.token) + }, + // 手动触发WebSocket消息处理 + simulateWebSocketMessage: () => { + const mockMessage = { + type: "PROCUREMENT_APPLICATION", + title: "新的采购申请", + content: "收到来自 测试用户 的设备采购申请:测试设备" + } + + const event = new MessageEvent('message', { + data: JSON.stringify(mockMessage) + }) + + if (socket && socket.onmessage) { + console.log('模拟WebSocket消息:', mockMessage) + socket.onmessage(event) + } else { + console.error('WebSocket连接不存在或onmessage未设置') + } + }, + // 强制重新连接WebSocket + reconnectWebSocket: () => { + console.log('强制重新连接WebSocket') + const token = getToken() + if (token) { + if (socket) { + socket.close() + socket = null + } + initWebSocket(token) + } else { + console.error('Token不存在,无法重新连接') + } + } + } + + // 暴露socket对象到全局,方便调试 + ;(window as any).socket = socket + ;(window as any).unreadMessageCount = unreadMessageCount +} + // 初始化 WebSocket - 使用防抖避免重复连接 let initTimer: NodeJS.Timeout | null = null const initWebSocket = (token: string) => { @@ -116,10 +259,12 @@ const initWebSocket = (token: string) => { } try { + // 修复WebSocket URL,确保使用正确的端口 const wsUrl = import.meta.env.VITE_API_WS_URL || 'ws://localhost:8888' - console.log('正在连接WebSocket:', `${wsUrl}/websocket?token=${token}`) + const wsEndpoint = wsUrl.replace('8000', '8888') // 确保使用8888端口 + console.log('正在连接WebSocket:', `${wsEndpoint}/websocket?token=${token}`) - socket = new WebSocket(`${wsUrl}/websocket?token=${token}`) + socket = new WebSocket(`${wsEndpoint}/websocket?token=${token}`) socket.onopen = () => { console.log('WebSocket连接成功') @@ -133,6 +278,10 @@ const initWebSocket = (token: string) => { // 处理通知消息 if (data.type && data.title && data.content) { console.log('处理通知消息:', data) + + // 播放语音提示 + playNotificationSound() + // 显示通知 Notification.info({ title: data.title, @@ -144,6 +293,9 @@ const initWebSocket = (token: string) => { // 增加未读消息计数 unreadMessageCount.value++ + + // 触发页面标题闪烁 + flashPageTitle() } else { // 处理简单的数字消息(兼容旧版本) const count = Number.parseInt(event.data) @@ -181,10 +333,17 @@ const initWebSocket = (token: string) => { const getMessageCount = async () => { try { const token = getToken() + console.log('获取到token:', token ? '存在' : '不存在') + if (token && !socket) { + console.log('准备初始化WebSocket连接') nextTick(() => { initWebSocket(token) }) + } else if (!token) { + console.warn('Token不存在,无法建立WebSocket连接') + } else if (socket) { + console.log('WebSocket连接已存在') } } catch (error) { console.error('Failed to get message count:', error) @@ -218,9 +377,32 @@ const logout = () => { onMounted(() => { nextTick(() => { - // getMessageCount() + // 立即尝试初始化WebSocket + getMessageCount() + + // 如果第一次失败,1秒后重试 + setTimeout(() => { + if (!socket) { + console.log('首次连接失败,重试WebSocket连接') + getMessageCount() + } + }, 1000) }) }) + +// 监听用户登录状态变化 +watch(() => userStore.token, (newToken, oldToken) => { + console.log('Token变化:', { oldToken: oldToken ? '存在' : '不存在', newToken: newToken ? '存在' : '不存在' }) + + if (newToken && !socket) { + console.log('用户登录,初始化WebSocket连接') + getMessageCount() + } else if (!newToken && socket) { + console.log('用户登出,关闭WebSocket连接') + socket.close() + socket = null + } +}, { immediate: true }) diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts index 369aad4..eab6be6 100644 --- a/src/types/auto-imports.d.ts +++ b/src/types/auto-imports.d.ts @@ -70,6 +70,6 @@ declare global { // for type re-export declare global { // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' + export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' import('vue') } diff --git a/src/views/operation-platform/data-processing/data-storage/components/DeformationTap.vue b/src/views/operation-platform/data-processing/data-storage/components/DeformationTap.vue new file mode 100644 index 0000000..336d713 --- /dev/null +++ b/src/views/operation-platform/data-processing/data-storage/components/DeformationTap.vue @@ -0,0 +1,125 @@ + + + + + \ No newline at end of file diff --git a/src/views/operation-platform/data-processing/data-storage/components/raw-data.vue b/src/views/operation-platform/data-processing/data-storage/components/raw-data.vue new file mode 100644 index 0000000..c80beb2 --- /dev/null +++ b/src/views/operation-platform/data-processing/data-storage/components/raw-data.vue @@ -0,0 +1,590 @@ + + + + + diff --git a/src/views/operation-platform/data-processing/data-storage/index.vue b/src/views/operation-platform/data-processing/data-storage/index.vue index 8b3d1e7..23de01b 100644 --- a/src/views/operation-platform/data-processing/data-storage/index.vue +++ b/src/views/operation-platform/data-processing/data-storage/index.vue @@ -1,7 +1,7 @@