Vue 连接 WebSocket wss://echo.websocket.org/ws

由于 wss://echo.websocket.org 不支持STOMP协议,改用原生WebSocket API
这个地址可以用来测试,wss://echo.websocket.org/ws
最终效果
image
代码如下:

<template>
  <div class="app-container">
    <el-row :gutter="10">
      <el-col :span="12">
        <el-card>
          <el-row>
            <el-col :span="18">
              <el-input v-model="socketEndpoint" style="width: 260px" />
              <el-button
                type="primary"
                class="ml-5"
                :disabled="isConnected"
                @click="connectWebSocket"
              >
                连接
              </el-button>
              <el-button type="danger" :disabled="!isConnected" @click="disconnectWebSocket">
                断开
              </el-button>
            </el-col>
            <el-col :span="6" class="text-right">
              连接状态:
              <el-tag v-if="isConnected" type="success">已连接</el-tag>
              <el-tag v-else type="info">已断开</el-tag>
            </el-col>
          </el-row>
        </el-card>
        <!-- 广播消息发送部分 -->
        <el-card class="mt-5">
          <el-form label-width="90px">
            <el-form-item label="消息内容">
              <el-input v-model="topicMessage" type="textarea" />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="sendToAll">发送广播</el-button>
            </el-form-item>
          </el-form>
        </el-card>
        <!-- 点对点消息发送部分 -->
        <el-card class="mt-5">
          <el-form label-width="90px">
            <el-form-item label="消息内容">
              <el-input v-model="queueMessage" type="textarea" />
            </el-form-item>
            <el-form-item label="消息接收人">
              <el-input v-model="receiver" />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="sendToUser">发送点对点消息</el-button>
            </el-form-item>
          </el-form>
        </el-card>
      </el-col>
      <!-- 消息接收显示部分 -->
      <el-col :span="12">
        <el-card>
          <div class="chat-messages-wrapper">
            <div
              v-for="(message, index) in messages"
              :key="index"
              :class="[
                message.type === 'tip' ? 'system-notice' : 'chat-message',
                {
                  'chat-message--sent': message.sender === 'sent',
                  'chat-message--received': message.sender !== 'received',
                },
              ]"
            >
              <template v-if="message.type != 'tip'">
                <div class="chat-message__content">
                  <div
                    :class="{
                      'chat-message__sender': message.sender === 'sent',
                      'chat-message__receiver': message.sender !== 'received',
                    }"
                  >
                    {{ message.sender }}
                  </div>
                  <div class="text-gray-600">{{ message.content }}</div>
                </div>
              </template>
              <div v-else>{{ message.content }}</div>
            </div>
          </div>
        </el-card>
      </el-col>
    </el-row>
  </div>
</template>

<script setup lang="ts">
// 用户信息类型
interface UserInfo {
  username: string;
}

// 消息类型
interface Message {
  sender: string;
  content: string;
  type: "sent" | "received" | "tip";
}

// 模拟用户信息
const userInfo: UserInfo = {
  username: "vue-user",
};

// WebSocket 地址
const socketEndpoint = ref("wss://echo.websocket.org/ws");
// 连接状态
const isConnected = ref(false);
// 消息列表
const messages = ref<Message[]>([]);
// 广播消息内容
const topicMessage = ref("亲爱的朋友们,这是一条广播消息示例!");
// 点对点消息内容
const queueMessage = ref("Hi, 这是一条点对点消息示例!");
const receiver = ref("user2");

// WebSocket 实例
let websocket: WebSocket | null = null;

// 连接 WebSocket
const connectWebSocket = (): void => {
  if (isConnected.value) {
    ElMessage.warning("WebSocket 已连接");
    return;
  }

  try {
    websocket = new WebSocket(socketEndpoint.value);

    websocket.onopen = (): void => {
      isConnected.value = true;
      messages.value.push({
        sender: "Server",
        content: "WebSocket 连接已建立",
        type: "tip",
      });
      ElMessage.success("WebSocket 连接成功");
    };

    websocket.onmessage = (event: MessageEvent): void => {
      messages.value.push({
        sender: "Server",
        content: event.data,
        type: "received",
      });
    };

    websocket.onerror = (error: Event): void => {
      console.error("WebSocket 错误:", error);
      ElMessage.error("WebSocket 连接发生错误");
    };

    websocket.onclose = (): void => {
      isConnected.value = false;
      messages.value.push({
        sender: "Server",
        content: "WebSocket 连接已关闭",
        type: "tip",
      });
      ElMessage.info("WebSocket 连接已关闭");
    };
  } catch (error) {
    console.error("创建 WebSocket 连接失败:", error);
    ElMessage.error("创建 WebSocket 连接失败");
  }
};

// 断开 WebSocket
const disconnectWebSocket = (): void => {
  if (websocket && isConnected.value) {
    websocket.close();
    websocket = null;
    isConnected.value = false;
  }
};

// 发送广播消息
const sendToAll = (): void => {
  if (!isConnected.value || !websocket) {
    ElMessage.warning("请先连接 WebSocket");
    return;
  }

  if (!topicMessage.value.trim()) {
    ElMessage.warning("消息内容不能为空");
    return;
  }

  websocket.send(topicMessage.value);
  messages.value.push({
    sender: userInfo.username,
    content: topicMessage.value,
    type: "sent",
  });
};

// 发送点对点消息
const sendToUser = (): void => {
  if (!isConnected.value || !websocket) {
    ElMessage.warning("请先连接 WebSocket");
    return;
  }

  if (!queueMessage.value.trim()) {
    ElMessage.warning("消息内容不能为空");
    return;
  }

  // 注意:wss://echo.websocket.org 会原样返回消息,不会区分接收人
  // 这里只是模拟发送点对点消息
  const message = `[To: ${receiver.value}] ${queueMessage.value}`;
  websocket.send(message);
  messages.value.push({
    sender: userInfo.username,
    content: message,
    type: "sent",
  });
};

onMounted(() => {
  // 可以设置自动连接
  // connectWebSocket();
});

onBeforeUnmount(() => {
  disconnectWebSocket();
});
</script>

<style scoped lang="scss">
.chat-messages-wrapper {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.chat-message {
  max-width: 80%;
  padding: 10px;
  border-radius: 5px;
  &--sent {
    align-self: flex-end;
    background-color: #dcf8c6;
  }
  &--received {
    align-self: flex-start;
    background-color: #e8e8e8;
  }
  &__content {
    display: flex;
    flex-direction: column;
    color: var(--el-text-color-primary); // 使用主题文本颜色
  }
  &__sender {
    margin-bottom: 5px;
    font-weight: bold;
    text-align: right;
  }
  &__receiver {
    margin-bottom: 5px;
    font-weight: bold;
    text-align: left;
  }
}
.system-notice {
  align-self: center;
  padding: 5px 10px;
  font-size: 0.9em;
  color: var(--el-text-color-secondary);
  background-color: var(--el-fill-color-lighter);
  border-radius: 15px;
}
</style>

posted @ 2025-08-26 11:37  VipSoft  阅读(27)  评论(0)    收藏  举报