2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
import * as Paho from 'paho-mqtt';
|
|
|
|
|
|
|
|
|
|
|
|
// MQTT消息类型定义
|
|
|
|
|
|
export interface MqttMessage {
|
|
|
|
|
|
topic: string;
|
|
|
|
|
|
payload: string | ArrayBuffer;
|
|
|
|
|
|
qos: number;
|
|
|
|
|
|
retained: boolean;
|
|
|
|
|
|
time: Date;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 订阅选项
|
|
|
|
|
|
export interface SubscribeOptions {
|
|
|
|
|
|
qos: 0 | 1 | 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-19 09:33:56 +08:00
|
|
|
|
// 根据当前页面协议自动选择MQTT配置
|
|
|
|
|
|
const getMqttConfig = () => {
|
|
|
|
|
|
// 检测当前页面协议(http: 或 https:)
|
|
|
|
|
|
const isHttps = window.location.protocol === 'https:';
|
2025-09-19 10:56:49 +08:00
|
|
|
|
console.log(isHttps,'检测环境');
|
|
|
|
|
|
|
2025-09-19 09:33:56 +08:00
|
|
|
|
return {
|
|
|
|
|
|
// 自动切换协议:https页面用wss,http页面用ws
|
|
|
|
|
|
protocol: isHttps ? 'wss' : 'ws',
|
|
|
|
|
|
host: 'www.cnxhyc.com',
|
|
|
|
|
|
// 自动切换端口:https对应9084,http对应9083
|
|
|
|
|
|
port: isHttps ? 9084 : 9083,
|
|
|
|
|
|
// 认证信息
|
|
|
|
|
|
username: 'admin',
|
|
|
|
|
|
password: '#YtvpSfCNG',
|
|
|
|
|
|
clientId: `vue3-mqtt-client-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`,
|
|
|
|
|
|
cleanSession: true,
|
|
|
|
|
|
keepAliveInterval: 60,
|
|
|
|
|
|
reconnect: true,
|
|
|
|
|
|
};
|
2025-09-04 13:35:39 +08:00
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
const MQTT_CONFIG = getMqttConfig();
|
2025-09-04 13:35:39 +08:00
|
|
|
|
|
|
|
|
|
|
// MQTT客户端组合式API
|
|
|
|
|
|
export function useMqtt() {
|
|
|
|
|
|
// 客户端实例
|
|
|
|
|
|
let client: Paho.Client | null = null;
|
|
|
|
|
|
// 状态管理(修复:已导入ref)
|
|
|
|
|
|
const connected = ref(false);
|
|
|
|
|
|
const connecting = ref(false);
|
|
|
|
|
|
const error = ref<Error | null>(null);
|
|
|
|
|
|
const messages = ref<MqttMessage[]>([]);
|
|
|
|
|
|
const subscribedTopics = ref<string[]>([]);
|
|
|
|
|
|
// 事件回调
|
|
|
|
|
|
const connectCallbacks: (() => void)[] = [];
|
|
|
|
|
|
const messageCallbacks: ((message: MqttMessage) => void)[] = [];
|
|
|
|
|
|
const errorCallbacks: ((err: Error) => void)[] = [];
|
|
|
|
|
|
const disconnectCallbacks: (() => void)[] = [];
|
|
|
|
|
|
// 修复:移除无用的p0参数,connect方法不接受回调(通过onConnect注册)
|
|
|
|
|
|
const connect = () => {
|
|
|
|
|
|
if (connected.value || connecting.value) return;
|
|
|
|
|
|
connecting.value = true;
|
|
|
|
|
|
error.value = null;
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
try {
|
2025-09-19 10:56:49 +08:00
|
|
|
|
// 打印当前使用的配置(方便调试)
|
|
|
|
|
|
console.log('当前MQTT连接配置:', {
|
|
|
|
|
|
protocol: MQTT_CONFIG.protocol,
|
|
|
|
|
|
host: MQTT_CONFIG.host,
|
|
|
|
|
|
port: MQTT_CONFIG.port,
|
|
|
|
|
|
clientId: MQTT_CONFIG.clientId
|
|
|
|
|
|
});
|
2025-09-04 13:35:39 +08:00
|
|
|
|
client = new Paho.Client(
|
2025-09-19 10:56:49 +08:00
|
|
|
|
MQTT_CONFIG.host,
|
2025-09-04 13:35:39 +08:00
|
|
|
|
MQTT_CONFIG.port,
|
|
|
|
|
|
MQTT_CONFIG.clientId
|
|
|
|
|
|
);
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 设置连接选项
|
|
|
|
|
|
const connectOptions: Paho.ConnectOptions = {
|
|
|
|
|
|
userName: MQTT_CONFIG.username,
|
|
|
|
|
|
password: MQTT_CONFIG.password,
|
|
|
|
|
|
cleanSession: MQTT_CONFIG.cleanSession,
|
|
|
|
|
|
keepAliveInterval: MQTT_CONFIG.keepAliveInterval,
|
|
|
|
|
|
reconnect: MQTT_CONFIG.reconnect,
|
2025-09-19 09:33:56 +08:00
|
|
|
|
useSSL: MQTT_CONFIG.protocol === 'wss', // 关键:根据协议自动启用 SSL
|
|
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 连接成功回调
|
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
|
console.log('MQTT连接成功');
|
|
|
|
|
|
connected.value = true;
|
|
|
|
|
|
connecting.value = false;
|
|
|
|
|
|
connectCallbacks.forEach(cb => cb()); // 触发所有连接成功回调
|
|
|
|
|
|
},
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 连接失败回调
|
|
|
|
|
|
onFailure: (err) => {
|
|
|
|
|
|
console.error('MQTT连接失败:', err);
|
|
|
|
|
|
error.value = new Error(err.errorMessage || '连接失败');
|
|
|
|
|
|
connected.value = false;
|
|
|
|
|
|
connecting.value = false;
|
|
|
|
|
|
errorCallbacks.forEach(cb => cb(error.value!));
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 设置客户端回调
|
|
|
|
|
|
client.onConnectionLost = (responseObject) => {
|
|
|
|
|
|
if (responseObject.errorCode !== 0) {
|
|
|
|
|
|
console.error('连接丢失:', responseObject.errorMessage);
|
|
|
|
|
|
error.value = new Error(responseObject.errorMessage || '连接丢失');
|
|
|
|
|
|
errorCallbacks.forEach(cb => cb(error.value!));
|
|
|
|
|
|
}
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
connected.value = false;
|
|
|
|
|
|
connecting.value = false;
|
|
|
|
|
|
disconnectCallbacks.forEach(cb => cb());
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 消息接收回调
|
|
|
|
|
|
client.onMessageArrived = (message) => {
|
|
|
|
|
|
const newMessage: MqttMessage = {
|
|
|
|
|
|
topic: message.destinationName,
|
|
|
|
|
|
payload: message.payloadString || message.payloadBytes,
|
|
|
|
|
|
qos: message.qos,
|
|
|
|
|
|
retained: message.retained,
|
|
|
|
|
|
time: new Date()
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
messages.value.push(newMessage);
|
|
|
|
|
|
messageCallbacks.forEach(cb => cb(newMessage));
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 连接服务器
|
|
|
|
|
|
client.connect(connectOptions);
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error('MQTT连接异常:', err);
|
|
|
|
|
|
error.value = err as Error;
|
|
|
|
|
|
connected.value = false;
|
|
|
|
|
|
connecting.value = false;
|
|
|
|
|
|
errorCallbacks.forEach(cb => cb(error.value!));
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 断开连接
|
|
|
|
|
|
const disconnect = () => {
|
|
|
|
|
|
if (!client || !connected.value) return;
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
client.disconnect();
|
|
|
|
|
|
client = null;
|
|
|
|
|
|
connected.value = false;
|
|
|
|
|
|
subscribedTopics.value = [];
|
|
|
|
|
|
disconnectCallbacks.forEach(cb => cb());
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 订阅主题
|
|
|
|
|
|
const subscribe = (topic: string, options: SubscribeOptions): Promise<void> => {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
if (!client || !connected.value) {
|
|
|
|
|
|
reject(new Error('未连接到MQTT服务器'));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
if (subscribedTopics.value.includes(topic)) {
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
client.subscribe(topic, {
|
|
|
|
|
|
qos: options.qos,
|
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
|
console.log(`订阅主题成功: ${topic}`);
|
|
|
|
|
|
subscribedTopics.value.push(topic);
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
},
|
|
|
|
|
|
onFailure: (err) => {
|
|
|
|
|
|
console.error(`订阅主题失败: ${topic}`, err);
|
|
|
|
|
|
reject(new Error(err.errorMessage || `订阅主题 ${topic} 失败`));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 取消订阅
|
|
|
|
|
|
const unsubscribe = (topic: string): Promise<void> => {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
if (!client || !connected.value) {
|
|
|
|
|
|
reject(new Error('未连接到MQTT服务器'));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
if (!subscribedTopics.value.includes(topic)) {
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
client.unsubscribe(topic, {
|
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
|
console.log(`取消订阅主题成功: ${topic}`);
|
|
|
|
|
|
subscribedTopics.value = subscribedTopics.value.filter(t => t !== topic);
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
},
|
|
|
|
|
|
onFailure: (err) => {
|
|
|
|
|
|
console.error(`取消订阅主题失败: ${topic}`, err);
|
|
|
|
|
|
reject(new Error(err.errorMessage || `取消订阅主题 ${topic} 失败`));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 发布消息
|
|
|
|
|
|
const publish = (
|
|
|
|
|
|
topic: string,
|
|
|
|
|
|
payload: string | ArrayBuffer,
|
|
|
|
|
|
options: { qos: 0 | 1 | 2; retained?: boolean }
|
|
|
|
|
|
): Promise<void> => {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
if (!client || !connected.value) {
|
|
|
|
|
|
reject(new Error('未连接到MQTT服务器'));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
if (!topic) {
|
|
|
|
|
|
reject(new Error('主题不能为空'));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
const message = new Paho.Message(
|
|
|
|
|
|
typeof payload === 'string' ? payload : payload.toString()
|
|
|
|
|
|
);
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
message.destinationName = topic;
|
|
|
|
|
|
message.qos = options.qos;
|
|
|
|
|
|
message.retained = options.retained ?? false;
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
client.send(message);
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 事件注册
|
|
|
|
|
|
const onConnect = (callback: () => void) => {
|
|
|
|
|
|
connectCallbacks.push(callback);
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
const onMessage = (callback: (message: MqttMessage) => void) => {
|
|
|
|
|
|
messageCallbacks.push(callback);
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
const onError = (callback: (err: Error) => void) => {
|
|
|
|
|
|
errorCallbacks.push(callback);
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
const onDisconnect = (callback: () => void) => {
|
|
|
|
|
|
disconnectCallbacks.push(callback);
|
|
|
|
|
|
};
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
// 组件卸载时断开连接
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
disconnect();
|
|
|
|
|
|
});
|
2025-09-19 09:33:56 +08:00
|
|
|
|
|
2025-09-04 13:35:39 +08:00
|
|
|
|
return {
|
|
|
|
|
|
connected,
|
|
|
|
|
|
connecting,
|
|
|
|
|
|
error,
|
|
|
|
|
|
messages,
|
|
|
|
|
|
subscribedTopics,
|
|
|
|
|
|
connect,
|
|
|
|
|
|
disconnect,
|
|
|
|
|
|
subscribe,
|
|
|
|
|
|
unsubscribe,
|
|
|
|
|
|
publish,
|
|
|
|
|
|
onConnect,
|
|
|
|
|
|
onMessage,
|
|
|
|
|
|
onError,
|
|
|
|
|
|
onDisconnect
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|