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

1 month ago
// 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
};
}