npm install socket.io-client
使用socket.io-client
import { io, type Socket } from "socket.io-client";
import { ref } from "vue";
interface WbeSocketOptions {
autoConnect?: boolean;
reconnectionAttempts?: number;
reconnectionDelay?: number;
}
type WebSocketStatus = "connecting" | "connected" | "disconnected" | "error";
type EventCallback<T=any> = (data: T) => void;
export default function useWebSocket(
url: string,
options: WbeSocketOptions = {}
) {
const {
autoConnect = true,
reconnectionAttempts = 5,
reconnectionDelay = 3000
} = options;
// 响应式状态
const socket = ref<Socket | null>(null);
const connectionStatus = ref<WebSocketStatus>("disconnected");
const lastError = ref<Error | null>(null);
const eventCallbacks = new Map<string, EventCallback>();
// 初始化Socket.IO
const initSocket = () => {
socket.value = io(url, { autoConnect, reconnectionAttempts, reconnectionDelay });
socket.value.on("connect", () => {
connectionStatus.value = "connected";
lastError.value = null;
});
socket.value.on("disconnect", () => {
connectionStatus.value = "disconnected";
});
socket.value.on("error", (error: Error) => {
connectionStatus.value = "error";
lastError.value = error;
});
// 通用的消息处理
socket.value.onAny((eventName: string, data: any) => {
const callback = eventCallbacks.get(eventName);
if (callback) {
callback(data);
}
})
};
// 订阅事件
const subscribe = <T>(eventName:string, callback:EventCallback<T>) => {
eventCallbacks.set(eventName, callback);
}
// 取消订阅
const unsubscribe = (eventName: string) => {
eventCallbacks.delete(eventName);
}
// 手动连接
const connect = () => {
if (!socket.value) {
initSocket();
}
socket.value?.connect();
}
const disconnect = () => {
socket.value?.disconnect();
eventCallbacks.clear();
}
if(autoConnect) {
initSocket();
}
return {
socket,
connectionStatus,
lastError,
subscribe,
unsubscribe,
connect,
disconnect
}
}
使用
<template>
<div class="screen-block">
<Title>销售统计</Title>
<div style="width: 100%; height: 90%">
<v-chart :option="option" />
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import Title from "../Title.vue";
import * as echarts from "echarts";
import useWebSocket from "@/composables/useWebSocket";
const { subscribe } = useWebSocket('http://localhost:3000', {
autoConnect: true,
})
const option = ref({})
const initOption = () => {
option.value = {
tooltip: {
trigger: "axis",
axisPointer: {
type: "line",
z: 0,
lineStyle: {
color: "#2d3443",
},
},
},
//x轴
xAxis: {
splitLine: { show: false }, // 是否显示网格线
axisLine: { show: true }, // 是否显示轴线
type: "value", // 作为数据展示
},
//y轴
yAxis: {
type: "category",
data: [],
inverse: true, // y轴反向
axisLine: { show: true }, // 是否显示轴线
axisTick: { show: false }, // 是否显示刻度
axisLabel: {
color: "#fff",
},
},
grid: {
top: "3%",
right: "4%",
bottom: "3%",
left: "3%",
containLabel: true,
},
series: [
{
type: "bar",
label: {
show: true,
position: "right",
},
data: [],
barWidth: 22,
roundCap: true,
showBackground: true,
backgroundStyle: {
color: "rgba(220, 220, 220, 0.3)",
},
itemStyle: {
borderWidth: 0,
borderRadius: [0, 10, 10, 0],
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{
offset: 0,
color: "#00fffb",
},
{
offset: 1,
color: "#0061ce",
},
]),
},
},
],
};
}
onMounted(() => {
initOption()
})
const updateOption = (data: Array<{name:string, value:number}>) => {
const categories = data.map((item) => item.name)
const values = data.map((item) => item.value)
option.value = {
yAxis: {
data: categories,
},
series: [
{
data: values,
},
],
}
}
subscribe('salesData', updateOption)
</script>
<style lang="scss" scoped>
.screen-block {
width: 100%;
height: 100%;
}
</style>