755 lines
17 KiB
Vue
755 lines
17 KiB
Vue
<!--
|
||
Uniapp WebSocket 测试页面
|
||
用于在 Uniapp 项目中测试微信小程序的 WebSocket 功能
|
||
|
||
使用方法:
|
||
1. 在 Uniapp 项目的 pages 目录下创建 ws-test 目录
|
||
2. 将此文件保存为 ws-test.vue
|
||
3. 在 pages.json 中注册该页面
|
||
4. 在微信开发者工具中运行项目并访问该页面
|
||
-->
|
||
|
||
<template>
|
||
<view class="container">
|
||
<view class="header">
|
||
<text class="title">WebSocket 测试</text>
|
||
<view class="status" :class="statusClass">
|
||
{{ connectionStatus }}
|
||
</view>
|
||
</view>
|
||
|
||
<view class="settings">
|
||
<view class="form-item">
|
||
<text class="label">服务器地址:</text>
|
||
<input
|
||
class="input"
|
||
v-model="wsUrl"
|
||
placeholder="wss://your-domain.com/ws/aikefu"
|
||
/>
|
||
</view>
|
||
<view class="btn-group">
|
||
<button
|
||
class="btn btn-primary"
|
||
@click="connectWebSocket"
|
||
:disabled="connecting"
|
||
>
|
||
{{ connecting ? '连接中...' : '连接' }}
|
||
</button>
|
||
<button
|
||
class="btn btn-default"
|
||
@click="disconnectWebSocket"
|
||
:disabled="!connected"
|
||
>
|
||
断开连接
|
||
</button>
|
||
<button
|
||
class="btn btn-warn"
|
||
@click="reconnectWebSocket"
|
||
:disabled="connecting"
|
||
>
|
||
重连
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="message-area">
|
||
<scroll-view
|
||
class="message-list"
|
||
scroll-y="true"
|
||
:scroll-top="scrollTop"
|
||
@scroll="onScroll"
|
||
>
|
||
<view
|
||
v-for="(message, index) in messages"
|
||
:key="message.id"
|
||
class="message-item"
|
||
:class="message.type"
|
||
>
|
||
<view class="message-header">
|
||
<text class="sender">{{ message.sender }}</text>
|
||
<text class="time">{{ message.time }}</text>
|
||
</view>
|
||
<view class="message-content">
|
||
{{ formatMessage(message.content) }}
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<view class="input-area">
|
||
<textarea
|
||
class="message-input"
|
||
v-model="inputMessage"
|
||
placeholder="输入要发送的消息..."
|
||
@confirm="sendCustomMessage"
|
||
></textarea>
|
||
<view class="btn-group">
|
||
<button class="btn btn-sm" @click="sendPing">Ping</button>
|
||
<button class="btn btn-sm" @click="sendTestMessage">测试消息</button>
|
||
<button class="btn btn-sm btn-primary" @click="sendCustomMessage">发送</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
wsUrl: 'wss://your-domain.com/ws/aikefu', // 替换为你的 WebSocket 服务器地址
|
||
connectionStatus: '未连接',
|
||
connected: false,
|
||
connecting: false,
|
||
socketTask: null,
|
||
messages: [],
|
||
inputMessage: '',
|
||
scrollTop: 0,
|
||
autoScroll: true
|
||
};
|
||
},
|
||
|
||
computed: {
|
||
statusClass() {
|
||
if (this.connecting) return 'status-connecting';
|
||
if (this.connected) return 'status-connected';
|
||
return 'status-disconnected';
|
||
}
|
||
},
|
||
|
||
onLoad() {
|
||
// 页面加载时自动连接
|
||
this.connectWebSocket();
|
||
},
|
||
|
||
onUnload() {
|
||
// 页面卸载时断开连接
|
||
this.closeWebSocket();
|
||
},
|
||
|
||
onHide() {
|
||
// 页面隐藏时断开连接
|
||
this.closeWebSocket();
|
||
},
|
||
|
||
methods: {
|
||
// 连接 WebSocket
|
||
connectWebSocket() {
|
||
if (this.connected || this.connecting) return;
|
||
|
||
this.connecting = true;
|
||
this.updateStatus('连接中...');
|
||
|
||
try {
|
||
// 创建 WebSocket 连接 (Uniapp API)
|
||
this.socketTask = uni.connectSocket({
|
||
url: this.wsUrl,
|
||
header: {
|
||
'content-type': 'application/json'
|
||
},
|
||
method: 'GET',
|
||
success: (res) => {
|
||
console.log('WebSocket 连接请求发送成功', res);
|
||
},
|
||
fail: (err) => {
|
||
console.error('WebSocket 连接请求发送失败', err);
|
||
this.connecting = false;
|
||
this.updateStatus('连接失败');
|
||
this.addMessage('系统', 'WebSocket 连接请求发送失败: ' + JSON.stringify(err), 'error');
|
||
}
|
||
});
|
||
|
||
// 监听 WebSocket 连接打开
|
||
this.socketTask.onOpen((res) => {
|
||
console.log('WebSocket 连接已打开', res);
|
||
this.connected = true;
|
||
this.connecting = false;
|
||
this.updateStatus('已连接');
|
||
this.addMessage('系统', 'WebSocket 连接已打开', 'system');
|
||
});
|
||
|
||
// 监听 WebSocket 接收到服务器的消息
|
||
this.socketTask.onMessage((res) => {
|
||
console.log('收到服务器消息', res.data);
|
||
this.addMessage('服务器', res.data, 'received');
|
||
});
|
||
|
||
// 监听 WebSocket 连接关闭
|
||
this.socketTask.onClose((res) => {
|
||
console.log('WebSocket 连接已关闭', res);
|
||
this.connected = false;
|
||
this.connecting = false;
|
||
this.updateStatus('已断开');
|
||
this.addMessage('系统', 'WebSocket 连接已关闭', 'system');
|
||
});
|
||
|
||
// 监听 WebSocket 错误
|
||
this.socketTask.onError((res) => {
|
||
console.error('WebSocket 连接发生错误', res);
|
||
this.connected = false;
|
||
this.connecting = false;
|
||
this.updateStatus('连接错误');
|
||
this.addMessage('系统', 'WebSocket 连接发生错误: ' + JSON.stringify(res), 'error');
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('WebSocket 连接异常', error);
|
||
this.connecting = false;
|
||
this.updateStatus('连接异常');
|
||
this.addMessage('系统', 'WebSocket 连接异常: ' + error.message, 'error');
|
||
}
|
||
},
|
||
|
||
// 断开 WebSocket 连接
|
||
disconnectWebSocket() {
|
||
if (!this.connected && !this.connecting) return;
|
||
|
||
this.closeWebSocket();
|
||
this.connected = false;
|
||
this.connecting = false;
|
||
this.updateStatus('已断开');
|
||
this.addMessage('系统', 'WebSocket 连接已手动断开', 'system');
|
||
},
|
||
|
||
// 关闭 WebSocket 连接(内部使用)
|
||
closeWebSocket() {
|
||
if (this.socketTask) {
|
||
try {
|
||
this.socketTask.close({
|
||
code: 1000,
|
||
reason: '用户主动断开连接'
|
||
});
|
||
} catch (error) {
|
||
console.error('关闭 WebSocket 连接失败', error);
|
||
}
|
||
this.socketTask = null;
|
||
}
|
||
},
|
||
|
||
// 重连 WebSocket
|
||
reconnectWebSocket() {
|
||
this.closeWebSocket();
|
||
this.connected = false;
|
||
this.connecting = false;
|
||
|
||
// 延迟 1 秒后重连
|
||
setTimeout(() => {
|
||
this.connectWebSocket();
|
||
}, 1000);
|
||
},
|
||
|
||
// 发送 Ping 消息
|
||
sendPing() {
|
||
this.sendMessage(JSON.stringify({ action: 'ping' }));
|
||
},
|
||
|
||
// 发送测试消息
|
||
sendTestMessage() {
|
||
this.sendMessage(JSON.stringify({
|
||
message: '你好,这是 Uniapp 测试消息!',
|
||
action: 'test',
|
||
timestamp: Date.now()
|
||
}));
|
||
},
|
||
|
||
// 发送自定义消息
|
||
sendCustomMessage() {
|
||
if (!this.inputMessage.trim()) {
|
||
uni.showToast({
|
||
title: '请输入消息内容',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 尝试解析为 JSON
|
||
JSON.parse(this.inputMessage);
|
||
this.sendMessage(this.inputMessage);
|
||
} catch (error) {
|
||
// 不是 JSON,包装为普通消息
|
||
this.sendMessage(JSON.stringify({
|
||
message: this.inputMessage,
|
||
action: 'chat'
|
||
}));
|
||
}
|
||
|
||
// 清空输入框
|
||
this.inputMessage = '';
|
||
},
|
||
|
||
// 发送消息(通用方法)
|
||
sendMessage(message) {
|
||
if (!this.connected || !this.socketTask) {
|
||
uni.showToast({
|
||
title: 'WebSocket 未连接',
|
||
icon: 'none'
|
||
});
|
||
this.updateStatus('已断开');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 使用 Uniapp API 发送消息
|
||
this.socketTask.send({
|
||
data: message,
|
||
success: () => {
|
||
this.addMessage('我', message, 'sent');
|
||
},
|
||
fail: (err) => {
|
||
console.error('发送消息失败', err);
|
||
this.addMessage('系统', '发送消息失败: ' + JSON.stringify(err), 'error');
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('发送消息异常', error);
|
||
this.addMessage('系统', '发送消息异常: ' + error.message, 'error');
|
||
}
|
||
},
|
||
|
||
// 更新连接状态
|
||
updateStatus(status) {
|
||
this.connectionStatus = status;
|
||
},
|
||
|
||
// 添加消息到消息列表
|
||
addMessage(sender, content, type) {
|
||
const message = {
|
||
id: Date.now(),
|
||
sender,
|
||
content,
|
||
type,
|
||
time: this.formatTime(new Date())
|
||
};
|
||
|
||
this.messages.push(message);
|
||
|
||
// 自动滚动到底部
|
||
this.scrollToBottom();
|
||
},
|
||
|
||
// 滚动到底部
|
||
scrollToBottom() {
|
||
if (this.autoScroll) {
|
||
// 延迟执行以确保 DOM 已更新
|
||
this.$nextTick(() => {
|
||
uni.createSelectorQuery().in(this)
|
||
.select('.message-list')
|
||
.boundingClientRect((rect) => {
|
||
if (rect) {
|
||
this.scrollTop = rect.height;
|
||
}
|
||
})
|
||
.exec();
|
||
});
|
||
}
|
||
},
|
||
|
||
// 格式化时间
|
||
formatTime(date) {
|
||
const hours = date.getHours().toString().padStart(2, '0');
|
||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||
return `${hours}:${minutes}:${seconds}`;
|
||
},
|
||
|
||
// 格式化消息内容(美化 JSON)
|
||
formatMessage(content) {
|
||
try {
|
||
const parsed = JSON.parse(content);
|
||
return JSON.stringify(parsed, null, 2);
|
||
} catch (error) {
|
||
return content;
|
||
}
|
||
},
|
||
|
||
// 处理滚动事件
|
||
onScroll(e) {
|
||
const { scrollTop, scrollHeight, clientHeight } = e.detail;
|
||
// 判断是否滚动到底部附近
|
||
this.autoScroll = scrollTop + clientHeight >= scrollHeight - 20;
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
padding: 20rpx;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
padding-bottom: 20rpx;
|
||
border-bottom: 1rpx solid #eee;
|
||
}
|
||
|
||
.title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.status {
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 24rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.status-connecting {
|
||
background-color: #fff3cd;
|
||
color: #856404;
|
||
}
|
||
|
||
.status-connected {
|
||
background-color: #d4edda;
|
||
color: #155724;
|
||
}
|
||
|
||
.status-disconnected {
|
||
background-color: #f8d7da;
|
||
color: #721c24;
|
||
}
|
||
|
||
.settings {
|
||
background-color: #fff;
|
||
border-radius: 10rpx;
|
||
padding: 20rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.form-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.input {
|
||
height: 80rpx;
|
||
padding: 0 20rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 10rpx;
|
||
font-size: 28rpx;
|
||
background-color: #f9f9f9;
|
||
}
|
||
|
||
.btn-group {
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
gap: 15rpx;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.btn {
|
||
flex: 1;
|
||
min-width: 150rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
border-radius: 10rpx;
|
||
font-size: 28rpx;
|
||
border: none;
|
||
}
|
||
|
||
.btn-primary {
|
||
background-color: #007aff;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-default {
|
||
background-color: #f5f5f5;
|
||
color: #333;
|
||
border: 1rpx solid #ddd;
|
||
}
|
||
|
||
.btn-warn {
|
||
background-color: #ff3b30;
|
||
color: #fff;
|
||
}
|
||
|
||
.btn-sm {
|
||
height: 60rpx;
|
||
line-height: 60rpx;
|
||
font-size: 24rpx;
|
||
min-width: 100rpx;
|
||
}
|
||
|
||
.message-area {
|
||
flex: 1;
|
||
background-color: #fff;
|
||
border-radius: 10rpx;
|
||
margin-bottom: 20rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.message-list {
|
||
height: 100%;
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.message-item {
|
||
margin-bottom: 20rpx;
|
||
padding: 15rpx;
|
||
border-radius: 10rpx;
|
||
max-width: 80%;
|
||
}
|
||
|
||
.message-item.sent {
|
||
align-self: flex-end;
|
||
margin-left: auto;
|
||
background-color: #d4edda;
|
||
border-right: 4rpx solid #28a745;
|
||
}
|
||
|
||
.message-item.received {
|
||
align-self: flex-start;
|
||
background-color: #e9ecef;
|
||
border-left: 4rpx solid #007bff;
|
||
}
|
||
|
||
.message-item.system {
|
||
align-self: center;
|
||
background-color: #fff3cd;
|
||
border-left: 4rpx solid #856404;
|
||
max-width: 90%;
|
||
}
|
||
|
||
.message-item.error {
|
||
align-self: center;
|
||
background-color: #f8d7da;
|
||
border-left: 4rpx solid #dc3545;
|
||
max-width: 90%;
|
||
}
|
||
|
||
.message-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 10rpx;
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.message-content {
|
||
font-size: 28rpx;
|
||
line-height: 1.5;
|
||
word-break: break-all;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.input-area {
|
||
background-color: #fff;
|
||
border-radius: 10rpx;
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.message-input {
|
||
width: 100%;
|
||
min-height: 120rpx;
|
||
max-height: 200rpx;
|
||
padding: 20rpx;
|
||
margin-bottom: 20rpx;
|
||
border: 1rpx solid #ddd;
|
||
border-radius: 10rpx;
|
||
font-size: 28rpx;
|
||
background-color: #f9f9f9;
|
||
}
|
||
</style>
|
||
|
||
<script module="pages.json">
|
||
// 该模块仅用于示例,实际应在项目根目录的 pages.json 中配置
|
||
|
||
/*
|
||
{
|
||
"pages": [
|
||
{
|
||
"path": "pages/ws-test/ws-test",
|
||
"style": {
|
||
"navigationBarTitleText": "WebSocket 测试"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
*/
|
||
</script>
|
||
|
||
<script module="README">
|
||
/*
|
||
# Uniapp WebSocket 测试页面使用说明
|
||
|
||
## 功能说明
|
||
用于在 Uniapp 项目中测试微信小程序的 WebSocket 功能,支持连接管理、消息发送和接收。
|
||
|
||
## 使用方法
|
||
|
||
### 1. 创建页面
|
||
将本文件保存为 `pages/ws-test/ws-test.vue`
|
||
|
||
### 2. 配置页面路由
|
||
在 `pages.json` 中添加以下配置:
|
||
|
||
```json
|
||
{
|
||
"pages": [
|
||
{
|
||
"path": "pages/ws-test/ws-test",
|
||
"style": {
|
||
"navigationBarTitleText": "WebSocket 测试"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 3. 配置服务器地址
|
||
在 `ws-test.vue` 文件中修改 `wsUrl` 变量:
|
||
|
||
```javascript
|
||
data() {
|
||
return {
|
||
wsUrl: 'wss://your-domain.com/ws/aikefu', // 替换为你的 WebSocket 服务器地址
|
||
// ...
|
||
};
|
||
}
|
||
```
|
||
|
||
### 4. 运行测试
|
||
- 使用 HBuilderX 开发工具
|
||
- 选择 "运行" -> "运行到小程序模拟器" -> "微信开发者工具"
|
||
- 在微信开发者工具中访问该页面
|
||
|
||
## 注意事项
|
||
|
||
### 微信小程序限制
|
||
1. **域名配置**:需要在微信小程序管理后台配置合法域名
|
||
2. **协议支持**:仅支持 HTTPS/WSS 协议
|
||
3. **连接数限制**:最多同时存在 5 个 WebSocket 连接
|
||
|
||
### Uniapp API 特点
|
||
1. Uniapp 使用 `uni.connectSocket()` 替代微信小程序的 `wx.connectSocket()`
|
||
2. Uniapp 使用 `uni.createSelectorQuery()` 替代微信小程序的 `wx.createSelectorQuery()`
|
||
3. 页面生命周期钩子函数使用 Vue 组件的 `onLoad()`、`onUnload()`、`onHide()`
|
||
|
||
## 扩展功能
|
||
|
||
### 添加消息类型支持
|
||
可以根据业务需求扩展支持的消息类型:
|
||
|
||
```javascript
|
||
// 发送聊天消息
|
||
sendChatMessage() {
|
||
this.sendMessage(JSON.stringify({
|
||
action: 'chat',
|
||
message: this.inputMessage,
|
||
userId: 'user123',
|
||
timestamp: Date.now()
|
||
}));
|
||
}
|
||
|
||
// 发送指令消息
|
||
sendCommand(command, params) {
|
||
this.sendMessage(JSON.stringify({
|
||
action: 'command',
|
||
command,
|
||
params,
|
||
timestamp: Date.now()
|
||
}));
|
||
}
|
||
```
|
||
|
||
### 添加自动重连机制
|
||
可以实现更智能的自动重连机制:
|
||
|
||
```javascript
|
||
data() {
|
||
return {
|
||
// ...
|
||
reconnectAttempts: 0,
|
||
maxReconnectAttempts: 5,
|
||
reconnectDelay: 1000
|
||
};
|
||
},
|
||
|
||
methods: {
|
||
// 自动重连
|
||
autoReconnect() {
|
||
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||
this.addMessage('系统', '自动重连失败,已达到最大尝试次数', 'error');
|
||
return;
|
||
}
|
||
|
||
this.reconnectAttempts++;
|
||
this.addMessage('系统', `尝试自动重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`, 'system');
|
||
|
||
setTimeout(() => {
|
||
this.connectWebSocket();
|
||
}, this.reconnectDelay);
|
||
},
|
||
|
||
// 连接成功后重置重连计数
|
||
onConnectSuccess() {
|
||
this.reconnectAttempts = 0;
|
||
// ...
|
||
}
|
||
}
|
||
```
|
||
|
||
## 常见问题
|
||
|
||
### 1. 连接失败
|
||
**可能原因**:
|
||
- 服务器地址错误
|
||
- 未配置合法域名
|
||
- 服务器未启动或网络不可达
|
||
- 使用了 HTTP 协议而不是 HTTPS
|
||
|
||
**解决方案**:
|
||
- 检查服务器地址和端口
|
||
- 在微信小程序管理后台配置合法域名
|
||
- 确保服务器正常运行
|
||
- 切换到 WSS 协议
|
||
|
||
### 2. 消息发送失败
|
||
**可能原因**:
|
||
- WebSocket 连接已关闭
|
||
- 消息格式不正确
|
||
- 网络中断
|
||
|
||
**解决方案**:
|
||
- 检查连接状态,必要时重新连接
|
||
- 确保消息格式为 JSON 字符串
|
||
- 检查网络连接
|
||
|
||
### 3. 接收消息异常
|
||
**可能原因**:
|
||
- 服务器返回格式错误
|
||
- 客户端解析错误
|
||
|
||
**解决方案**:
|
||
- 检查服务器返回的消息格式
|
||
- 调试客户端解析逻辑
|
||
|
||
## 联系和支持
|
||
|
||
如果在使用过程中遇到问题,请联系开发人员或查看相关文档。
|
||
|
||
---
|
||
|
||
**更新时间**:2025-12-19
|
||
**版本**:1.0.0
|
||
**维护人员**:WebSocket 开发团队
|
||
*/
|
||
</script>
|