Luminous.X in the house  
Enjoy my whole life!

以下基于 Vue3 + Spring Boot 3.2 的 WebSocket 消息弹窗实现方案:

后端实现(Spring Boot 3.2)

  1. 添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
  1. WebSocket 配置类
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws-notification")
                .setAllowedOriginPatterns("*")
                .withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
        registry.setApplicationDestinationPrefixes("/app");
    }
}
  1. 消息通知服务
@Service
public class NotificationService {
    private final SimpMessagingTemplate messagingTemplate;

    public NotificationService(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    public void sendGlobalNotification(String message) {
        Map<String, Object> payload = new HashMap<>();
        payload.put("timestamp", System.currentTimeMillis());
        payload.put("content", message);
        payload.put("type", "ALERT");
        
        messagingTemplate.convertAndSend("/topic/notifications", payload);
    }
}
  1. 测试控制器(可选)
@RestController
@RequestMapping("/api/notify")
public class TestController {
    private final NotificationService notificationService;

    public TestController(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    @PostMapping
    public ResponseEntity<?> triggerNotification(@RequestParam String message) {
        notificationService.sendGlobalNotification(message);
        return ResponseEntity.ok().build();
    }
}

前端实现(Vue3)

  1. 安装依赖
npm install @stomp/stompjs sockjs-client
  1. WebSocket 工具类(src/utils/websocket.js)
import { reactive } from 'vue'
import { Client } from '@stomp/stompjs'

const useWebSocket = () => {
  const state = reactive({
    client: null,
    isConnected: false
  })

  const connect = () => {
    state.client = new Client({
      brokerURL: 'http://localhost:8080/ws-notification',
      debug: function (str) {
        console.log('STOMP: ' + str)
      },
      reconnectDelay: 5000,
      heartbeatIncoming: 4000,
      heartbeatOutgoing: 4000
    })

    state.client.onConnect = () => {
      state.isConnected = true
      state.client.subscribe('/topic/notifications', (message) => {
        const payload = JSON.parse(message.body)
        // 触发全局事件或更新store
        const event = new CustomEvent('global-notification', { detail: payload })
        window.dispatchEvent(event)
      })
    }

    state.client.activate()
  }

  const disconnect = () => {
    if (state.client) {
      state.client.deactivate()
      state.isConnected = false
    }
  }

  return { state, connect, disconnect }
}

export default useWebSocket
  1. 全局通知组件(src/components/GlobalNotification.vue)
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'

const notifications = ref([])
let timeoutId = null

const showNotification = (payload) => {
  notifications.value.push({
    id: Date.now(),
    ...payload
  })
  
  if (!timeoutId) {
    processQueue()
  }
}

const processQueue = () => {
  if (notifications.value.length === 0) {
    timeoutId = null
    return
  }

  timeoutId = setTimeout(() => {
    notifications.value.shift()
    processQueue()
  }, 3000)
}

onMounted(() => {
  window.addEventListener('global-notification', (e) => {
    showNotification(e.detail)
  })
})

onBeforeUnmount(() => {
  window.removeEventListener('global-notification')
  clearTimeout(timeoutId)
})
</script>

<template>
  <div class="fixed top-4 right-4 z-50 space-y-2">
    <div 
      v-for="notification in notifications"
      :key="notification.id"
      class="p-4 bg-white rounded-lg shadow-lg border-l-4 border-blue-500 min-w-[300px]"
    >
      <div class="font-semibold">{{ notification.type }}</div>
      <div class="text-gray-600">{{ notification.content }}</div>
      <div class="text-xs text-gray-400 mt-2">
        {{ new Date(notification.timestamp).toLocaleTimeString() }}
      </div>
    </div>
  </div>
</template>
  1. 在 App.vue 中初始化
<script setup>
import { onMounted } from 'vue'
import GlobalNotification from '@/components/GlobalNotification.vue'
import useWebSocket from '@/utils/websocket'

const { connect, disconnect } = useWebSocket()

onMounted(() => {
  connect()
})

// 可选:处理断线重连等
</script>

<template>
  <RouterView />
  <GlobalNotification />
</template>

使用流程

  1. 后端启动后,调用 /api/notify?message=测试消息 发送通知
  2. 前端会自动建立 WebSocket 连接
  3. 收到通知后会在右上角显示持续3秒的弹窗
  4. 多个通知会排队依次显示

扩展建议

  1. 添加消息类型样式(成功/警告/错误)
  2. 增加消息关闭按钮
  3. 添加音效提示
  4. 连接状态监控
  5. JWT 认证支持
  6. 消息持久化存储
  7. 用户特定的通知通道

测试技巧

  1. 使用 Postman 的 WebSocket 功能测试后端
  2. 在浏览器控制台测试事件触发:
window.dispatchEvent(new CustomEvent('global-notification', {
  detail: {
    timestamp: Date.now(),
    content: "测试消息",
    type: "SUCCESS"
  }
}))
posted on 2025-05-12 16:20  Luminous_X  阅读(275)  评论(0)    收藏  举报