实现websocket连接
This commit is contained in:
parent
0dc18eb8eb
commit
2a58e93f96
|
|
@ -6,21 +6,30 @@
|
|||
<view class="content">
|
||||
<view class="status-card">
|
||||
<view class="status-header">
|
||||
<view :class="['status-dot', socketConnected ? 'online' : 'offline']" />
|
||||
<view
|
||||
:class="['status-dot', socketConnected ? 'online' : 'offline']"
|
||||
/>
|
||||
<text class="status-title">{{ connectionText }}</text>
|
||||
</view>
|
||||
<text class="status-desc">
|
||||
请保持手机在线,等待刷卡设备将NFC卡号传递到本页面。
|
||||
</text>
|
||||
<view class="card-box" :class="{ ready: !!cardNo }">
|
||||
<view :class="{ ready: !!cardNo }" class="card-box">
|
||||
<text class="card-label">NFC卡号</text>
|
||||
<text class="card-value">{{ cardNo || "等待刷卡..." }}</text>
|
||||
</view>
|
||||
<view v-if="connectionError" class="error-text">{{ connectionError }}</view>
|
||||
<view v-if="connectionError" class="error-text">
|
||||
<text class="error-content">{{ connectionError }}</text>
|
||||
</view>
|
||||
<view v-else-if="lastMessage" class="hint-text">{{ lastMessage }}</view>
|
||||
<view class="status-actions">
|
||||
<view class="text-btn" @click="handleRetry">重新连接</view>
|
||||
<view class="text-btn" @click="resetCard" v-if="cardNo">清空卡号</view>
|
||||
<view v-if="cardNo" class="text-btn" @click="resetCard"
|
||||
>清空卡号
|
||||
</view>
|
||||
<view v-if="connectionError" class="text-btn" @click="testServerConnection"
|
||||
>测试服务器
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -65,10 +74,11 @@
|
|||
<script>
|
||||
import BaseBackground from "@/components/base-background/base-background.vue";
|
||||
import CustomNavbar from "@/components/custom-navbar/custom-navbar.vue";
|
||||
import { getRequestConfig } from "@/utils/request.js";
|
||||
import { getRequestConfig, getToken } from "@/utils/request.js";
|
||||
import { bindNfcCard } from "@/api/memorial/index.js";
|
||||
|
||||
const NFC_WS_PATH = "/ws/nfc/swipeCard";
|
||||
const WS_PATH = "/ws/device";
|
||||
const FIXED_MAC = "111111111111";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -87,6 +97,7 @@ export default {
|
|||
lastMessage: "",
|
||||
usingGlobalSocketEvents: false,
|
||||
globalSocketHandlers: null,
|
||||
connectTimeout: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -116,11 +127,39 @@ export default {
|
|||
buildSocketUrl() {
|
||||
try {
|
||||
const { baseUrl } = getRequestConfig();
|
||||
if (!baseUrl) return "";
|
||||
const protocol = baseUrl.startsWith("https") ? "wss" : "ws";
|
||||
if (!baseUrl) {
|
||||
console.error("buildSocketUrl: baseUrl 为空");
|
||||
return "";
|
||||
}
|
||||
|
||||
// 获取当前登录的 token
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
console.error("buildSocketUrl: token 为空,请先登录");
|
||||
this.connectionError = "未登录,请先登录后再试";
|
||||
return "";
|
||||
}
|
||||
|
||||
// 根据 baseUrl 的协议自动选择 ws 或 wss
|
||||
const isHttps = baseUrl.startsWith("https://");
|
||||
const protocol = isHttps ? "wss" : "ws";
|
||||
const host = baseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
||||
const query = this.unitId ? `?unitId=${this.unitId}` : "";
|
||||
return `${protocol}://${host}${NFC_WS_PATH}${query}`;
|
||||
|
||||
// 构建查询参数:token 和固定的 mac
|
||||
const query = `?token=${encodeURIComponent(token)}&mac=${FIXED_MAC}`;
|
||||
const url = `${protocol}://${host}${WS_PATH}${query}`;
|
||||
|
||||
console.log("构建 WebSocket URL:", {
|
||||
baseUrl,
|
||||
protocol,
|
||||
host,
|
||||
path: WS_PATH,
|
||||
token: token ? `${token.substring(0, 20)}...` : "无",
|
||||
mac: FIXED_MAC,
|
||||
finalUrl: url.replace(token, "***"), // 日志中隐藏完整token
|
||||
});
|
||||
|
||||
return url;
|
||||
} catch (error) {
|
||||
console.error("构建WebSocket地址失败", error);
|
||||
return "";
|
||||
|
|
@ -132,30 +171,76 @@ export default {
|
|||
const url = this.buildSocketUrl();
|
||||
if (!url) {
|
||||
this.connectionError = "缺少WebSocket地址,请检查配置";
|
||||
console.error("initSocket: URL 构建失败");
|
||||
return;
|
||||
}
|
||||
this.cleanupSocket();
|
||||
console.log("NFC配对页面发起WebSocket连接:", url);
|
||||
this.socketTask = uni.connectSocket({ url });
|
||||
if (!this.socketTask) {
|
||||
this.connectionError = "当前环境不支持WebSocket";
|
||||
return;
|
||||
}
|
||||
if (typeof this.socketTask.onOpen === "function") {
|
||||
this.bindTaskSocketEvents();
|
||||
} else {
|
||||
this.bindGlobalSocketEvents();
|
||||
|
||||
try {
|
||||
// 添加连接超时处理
|
||||
this.connectTimeout = setTimeout(() => {
|
||||
if (!this.socketConnected) {
|
||||
console.error("WebSocket 连接超时");
|
||||
this.connectionError = "连接超时,请检查网络和服务器状态";
|
||||
this.cleanupSocket();
|
||||
}
|
||||
}, 10000); // 10秒超时
|
||||
|
||||
this.socketTask = uni.connectSocket({
|
||||
url,
|
||||
success: (res) => {
|
||||
console.log("uni.connectSocket success:", res);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error("uni.connectSocket fail:", err);
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectionError = `连接失败: ${err.errMsg || "未知错误"}`;
|
||||
this.socketConnected = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.socketTask) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectionError = "当前环境不支持WebSocket";
|
||||
console.error("initSocket: socketTask 为 null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.socketTask.onOpen === "function") {
|
||||
console.log("使用 Task 级别事件绑定", this.socketTask);
|
||||
this.bindTaskSocketEvents();
|
||||
} else {
|
||||
console.log("使用全局事件绑定", this.socketTask);
|
||||
this.bindGlobalSocketEvents();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("initSocket 异常:", error);
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectionError = `连接异常: ${error.message || "未知错误"}`;
|
||||
}
|
||||
},
|
||||
cleanupSocket() {
|
||||
// 清除连接超时定时器
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = null;
|
||||
}
|
||||
|
||||
if (this.socketTask && typeof this.socketTask.close === "function") {
|
||||
try {
|
||||
this.socketTask.close();
|
||||
console.log("WebSocket 连接已关闭 (Task级别)");
|
||||
} catch (error) {
|
||||
console.warn("关闭WebSocket失败", error);
|
||||
}
|
||||
} else {
|
||||
uni.closeSocket && uni.closeSocket({});
|
||||
try {
|
||||
uni.closeSocket && uni.closeSocket({});
|
||||
console.log("WebSocket 连接已关闭 (全局)");
|
||||
} catch (error) {
|
||||
console.warn("关闭WebSocket失败", error);
|
||||
}
|
||||
}
|
||||
this.unbindGlobalSocketEvents();
|
||||
this.socketTask = null;
|
||||
|
|
@ -164,18 +249,54 @@ export default {
|
|||
bindTaskSocketEvents() {
|
||||
if (!this.socketTask) return;
|
||||
this.socketTask.onOpen(() => {
|
||||
console.log("NFC WebSocket 已连接");
|
||||
console.log("NFC WebSocket 已连接 (Task级别)");
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = null;
|
||||
}
|
||||
this.socketConnected = true;
|
||||
this.connectionError = "";
|
||||
this.lastMessage = "连接成功,等待刷卡...";
|
||||
});
|
||||
this.socketTask.onClose((event) => {
|
||||
console.warn("NFC WebSocket 连接关闭", event);
|
||||
console.warn("NFC WebSocket 连接关闭 (Task级别)", event);
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = null;
|
||||
}
|
||||
this.socketConnected = false;
|
||||
this.socketTask = null;
|
||||
if (!this.connectionError) {
|
||||
this.connectionError = "连接已断开";
|
||||
}
|
||||
});
|
||||
this.socketTask.onError((error) => {
|
||||
console.error("NFC WebSocket 错误", error);
|
||||
this.connectionError = "连接失败,请重试";
|
||||
console.error("NFC WebSocket 错误 (Task级别)", error);
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = null;
|
||||
}
|
||||
|
||||
// 解析错误信息
|
||||
let errorMsg = error.errMsg || error.message || "连接失败";
|
||||
let userFriendlyMsg = "连接失败";
|
||||
|
||||
// 处理 Invalid HTTP status 错误
|
||||
if (errorMsg.includes("Invalid HTTP status") || error.errCode === 1004) {
|
||||
userFriendlyMsg = "服务器不支持WebSocket或路径不存在\n请检查:\n1. 服务器是否正常运行\n2. WebSocket路径是否正确\n3. 服务器是否支持WebSocket协议";
|
||||
console.error("WebSocket握手失败,可能原因:", {
|
||||
url: this.buildSocketUrl(),
|
||||
errorCode: error.errCode,
|
||||
errorMsg: errorMsg,
|
||||
suggestion: "服务器可能返回了404或500错误,请检查服务器日志"
|
||||
});
|
||||
} else if (errorMsg.includes("timeout")) {
|
||||
userFriendlyMsg = "连接超时,请检查网络连接";
|
||||
} else if (errorMsg.includes("fail")) {
|
||||
userFriendlyMsg = "网络连接失败,请检查网络和服务器地址";
|
||||
}
|
||||
|
||||
this.connectionError = userFriendlyMsg;
|
||||
this.socketConnected = false;
|
||||
});
|
||||
this.socketTask.onMessage((event) => {
|
||||
|
|
@ -187,17 +308,53 @@ export default {
|
|||
this.usingGlobalSocketEvents = true;
|
||||
this.globalSocketHandlers = {
|
||||
open: () => {
|
||||
console.log("NFC WebSocket 已连接 (global handler)");
|
||||
console.log("NFC WebSocket 已连接 (全局事件)");
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = null;
|
||||
}
|
||||
this.socketConnected = true;
|
||||
this.connectionError = "";
|
||||
this.lastMessage = "连接成功,等待刷卡...";
|
||||
},
|
||||
close: (event) => {
|
||||
console.warn("NFC WebSocket 连接关闭", event);
|
||||
console.warn("NFC WebSocket 连接关闭 (全局事件)", event);
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = null;
|
||||
}
|
||||
this.socketConnected = false;
|
||||
if (!this.connectionError) {
|
||||
this.connectionError = "连接已断开";
|
||||
}
|
||||
},
|
||||
error: (error) => {
|
||||
console.error("NFC WebSocket 错误", error);
|
||||
this.connectionError = "连接失败,请重试";
|
||||
console.error("NFC WebSocket 错误 (全局事件)", error);
|
||||
if (this.connectTimeout) {
|
||||
clearTimeout(this.connectTimeout);
|
||||
this.connectTimeout = null;
|
||||
}
|
||||
|
||||
// 解析错误信息
|
||||
let errorMsg = error.errMsg || error.message || "连接失败";
|
||||
let userFriendlyMsg = "连接失败";
|
||||
|
||||
// 处理 Invalid HTTP status 错误
|
||||
if (errorMsg.includes("Invalid HTTP status") || error.errCode === 1004) {
|
||||
userFriendlyMsg = "服务器不支持WebSocket或路径不存在\n请检查:\n1. 服务器是否正常运行\n2. WebSocket路径是否正确\n3. 服务器是否支持WebSocket协议";
|
||||
console.error("WebSocket握手失败,可能原因:", {
|
||||
url: this.buildSocketUrl(),
|
||||
errorCode: error.errCode,
|
||||
errorMsg: errorMsg,
|
||||
suggestion: "服务器可能返回了404或500错误,请检查服务器日志"
|
||||
});
|
||||
} else if (errorMsg.includes("timeout")) {
|
||||
userFriendlyMsg = "连接超时,请检查网络连接";
|
||||
} else if (errorMsg.includes("fail")) {
|
||||
userFriendlyMsg = "网络连接失败,请检查网络和服务器地址";
|
||||
}
|
||||
|
||||
this.connectionError = userFriendlyMsg;
|
||||
this.socketConnected = false;
|
||||
},
|
||||
message: (event) => {
|
||||
|
|
@ -264,6 +421,89 @@ export default {
|
|||
handleRetry() {
|
||||
this.initSocket();
|
||||
},
|
||||
// 测试服务器连接(用于诊断)
|
||||
async testServerConnection() {
|
||||
try {
|
||||
const { baseUrl } = getRequestConfig();
|
||||
console.log("测试服务器连接:", baseUrl);
|
||||
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
uni.showModal({
|
||||
title: "未登录",
|
||||
content: "请先登录后再测试服务器连接",
|
||||
showCancel: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 测试 WebSocket 服务器是否可达
|
||||
const isHttps = baseUrl.startsWith("https://");
|
||||
const protocol = isHttps ? "wss" : "ws";
|
||||
const host = baseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
||||
const testUrl = `${protocol}://${host}${WS_PATH}?token=${encodeURIComponent(token)}&mac=${FIXED_MAC}`;
|
||||
|
||||
console.log("测试 WebSocket URL:", testUrl.replace(token, "***"));
|
||||
|
||||
uni.showLoading({ title: "测试连接中...", mask: true });
|
||||
|
||||
// 尝试连接 WebSocket 来测试服务器
|
||||
const testSocket = uni.connectSocket({
|
||||
url: testUrl,
|
||||
success: () => {
|
||||
console.log("WebSocket 连接测试:连接请求已发送");
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error("WebSocket 连接测试失败:", err);
|
||||
uni.hideLoading();
|
||||
uni.showModal({
|
||||
title: "服务器连接测试",
|
||||
content: `无法连接到 WebSocket 服务器\n错误: ${err.errMsg || "未知错误"}\n\n请检查:\n1. 服务器地址是否正确 (${host})\n2. WebSocket 服务是否运行\n3. 网络是否正常\n4. Token 是否有效`,
|
||||
showCancel: false,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// 设置超时
|
||||
const timeout = setTimeout(() => {
|
||||
testSocket.close();
|
||||
uni.hideLoading();
|
||||
uni.showModal({
|
||||
title: "连接超时",
|
||||
content: `WebSocket 连接超时\n\n请检查:\n1. 服务器地址: ${host}\n2. WebSocket 路径: ${WS_PATH}\n3. 服务器是否正常运行`,
|
||||
showCancel: false,
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
testSocket.onOpen(() => {
|
||||
clearTimeout(timeout);
|
||||
testSocket.close();
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "服务器连接正常",
|
||||
icon: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
|
||||
testSocket.onError((err) => {
|
||||
clearTimeout(timeout);
|
||||
uni.hideLoading();
|
||||
uni.showModal({
|
||||
title: "服务器连接测试",
|
||||
content: `WebSocket 连接失败\n错误: ${err.errMsg || "未知错误"}\n\n请检查:\n1. 服务器地址是否正确\n2. WebSocket 服务是否运行\n3. Token 是否有效`,
|
||||
showCancel: false,
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("测试连接异常:", error);
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: "测试失败",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
},
|
||||
async handleBind() {
|
||||
if (!this.canSubmit || this.binding) return;
|
||||
this.binding = true;
|
||||
|
|
@ -385,8 +625,18 @@ export default {
|
|||
|
||||
.error-text {
|
||||
margin-top: 16rpx;
|
||||
padding: 16rpx;
|
||||
background: #fef0f0;
|
||||
border-radius: 12rpx;
|
||||
border-left: 4rpx solid #f56c6c;
|
||||
}
|
||||
|
||||
.error-content {
|
||||
color: #f56c6c;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.6;
|
||||
white-space: pre-line;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.hint-text {
|
||||
|
|
@ -461,4 +711,3 @@ export default {
|
|||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user