You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
4.4 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// composables/useSignalR.js
import { ref, onMounted, onUnmounted } from 'vue';
import { HubConnection, HubConnectionBuilder, HttpTransportType, LogLevel } from '@microsoft/signalr';
export function useSignalR() {
const connection = ref(null) // 存储连接实例
const connectionId = ref('') // 存储 connectionId
const isConnected = ref(false) // 连接状态
const sigMsg = ref('');
const error = ref(null);
const authStore = useAuthStore();
const config = useRuntimeConfig();
// 消息处理器集合(支持多个监听器)
const messageHandlers = ref({});
const hubUrl = config.public.signalRUrl;
// 连接配置
const connect = async () => {
try {
connection.value = new HubConnectionBuilder()
.withUrl(hubUrl, {
accessTokenFactory: () => {
if (authStore.token) {
authStore.initializeAuth();
}
const token = authStore.token; // 使用 Nuxt 的 useCookie 获取 Token
// console.log("signalr获取到的token:", token)
if (token) {
return token;
} else {
return '';
}
},
skipNegotiation: true, // 仅当使用 CORS 时需要
transport: HttpTransportType.WebSockets, // 优先 WebSocket
// httpClient: {
// fetch: async (url, options) => {
// // 添加 Token 到请求头
// options.headers = {
// ...options.headers,
// Authorization: `Bearer ${useCookie('token')?.value}`
// }
// return fetch(url, options)
// }
// }
})
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: (retryContext) => {
// 自定义重连间隔(例如:首次 2s之后 5s、10s...
return retryContext.retryCount === 0 ? 2000 : 5000 * retryContext.retryCount;
}
})
.configureLogging(LogLevel.Information)
.build();
// 注册消息处理
connection.value.on('ReceiveProgress', (progress, msg) => {
sigMsg.value = JSON.stringify({ progress, msg });
// messages.value.push({ progress, msg, time: new Date() });
// 触发自定义事件(可选)
if (messageHandlers.value['ReceiveProgress']) {
messageHandlers.value['ReceiveProgress'](progress, msg);
}
});
connection.value.on("ReceiveConnectionId", (conId) => {
connectionId.value = conId;
});
// 连接状态变更
connection.value.onclose((err) => {
isConnected.value = false;
error.value = err?.message || '连接意外断开';
console.error('SignalR 连接关闭,重新连接:', err);
connect();
});
// 启动连接
await connection.value.start();
isConnected.value = true;
error.value = null;
} catch (err) {
isConnected.value = false;
connectionId.value = '';
error.value = err.message || '连接失败';
console.error('SignalR 连接启动失败:', err);
}
};
// 断开连接
const disconnect = async () => {
if (connection.value) {
await connection.value.stop();
isConnected.value = false;
connectionId.value = '';
}
};
// 发送消息
const sendMessage = async (method, ...args) => {
if (!isConnected.value) {
throw new Error('未连接到 SignalR 服务');
}
try {
await connection.value.invoke(method, ...args);
} catch (err) {
error.value = `发送失败: ${err.message}`;
throw err;
}
};
// 注册消息监听器(支持自定义事件)
const on = (eventName, callback) => {
if (!messageHandlers.value[eventName]) {
messageHandlers.value[eventName] = [];
}
messageHandlers.value[eventName].push(callback);
};
// 移除监听器
const off = (eventName, callback) => {
if (messageHandlers.value[eventName]) {
messageHandlers.value[eventName] = messageHandlers.value[eventName].filter(cb => cb !== callback);
}
};
// 生命周期钩子:自动连接
onMounted(() => {
connect();
});
// 生命周期钩子:组件卸载时断开
onUnmounted(() => {
disconnect();
messageHandlers.value = {}; // 清理监听器
});
return {
isConnected,
sigMsg,
error,
connectionId,
connect,
disconnect,
sendMessage,
on,
off
};
}