326 lines
12 KiB
Vue
326 lines
12 KiB
Vue
<script setup lang="ts">
|
|
import {
|
|
EnvironmentOutlined,
|
|
SafetyOutlined,
|
|
HddOutlined,
|
|
GroupOutlined,
|
|
UserAddOutlined,
|
|
UserSwitchOutlined
|
|
} from '@ant-design/icons-vue';
|
|
import { computed, ref, onMounted } from 'vue';
|
|
import { getDashboardOverview } from '@/service/api/auth';
|
|
|
|
defineOptions({
|
|
name: 'HeaderBanner'
|
|
});
|
|
|
|
const overviewData = ref<Api.DashboardOverview>({
|
|
cloud: { connected: false },
|
|
siteNum: 0,
|
|
totalGatewayNum: 0,
|
|
connectedGatewayNum: 0,
|
|
disconnectedGatewayNum: 0,
|
|
totalSwitchNum: 0,
|
|
connectedSwitchNum: 0,
|
|
disconnectedSwitchNum: 0,
|
|
totalApNum: 0,
|
|
connectedApNum: 0,
|
|
disconnectedApNum: 0,
|
|
isolatedApNum: 0,
|
|
totalClientNum: 0,
|
|
wiredClientNum: 0,
|
|
wirelessClientNum: 0,
|
|
guestNum: 0,
|
|
registerUserNum: 0,
|
|
onlineUserNum: 0
|
|
});
|
|
|
|
const fetchOverviewData = async () => {
|
|
try {
|
|
const { data } = await getDashboardOverview();
|
|
console.log('Raw API response:', data);
|
|
if (data) {
|
|
overviewData.value = {
|
|
cloud: { connected: data.cloud?.connected ?? false },
|
|
siteNum: data.siteNum ?? 0,
|
|
totalGatewayNum: data.totalGatewayNum ?? 0,
|
|
connectedGatewayNum: data.connectedGatewayNum ?? 0,
|
|
disconnectedGatewayNum: data.disconnectedGatewayNum ?? 0,
|
|
totalSwitchNum: data.totalSwitchNum ?? 0,
|
|
connectedSwitchNum: data.connectedSwitchNum ?? 0,
|
|
disconnectedSwitchNum: data.disconnectedSwitchNum ?? 0,
|
|
totalApNum: data.totalApNum ?? 0,
|
|
connectedApNum: data.connectedApNum ?? 0,
|
|
disconnectedApNum: data.disconnectedApNum ?? 0,
|
|
isolatedApNum: data.isolatedApNum ?? 0,
|
|
totalClientNum: data.totalClientNum ?? 0,
|
|
wiredClientNum: data.wiredClientNum ?? 0,
|
|
wirelessClientNum: data.wirelessClientNum ?? 0,
|
|
guestNum: data.guestNum ?? 0,
|
|
registerUserNum: data.registerUserNum ?? 0,
|
|
onlineUserNum: data.onlineUserNum ?? 0
|
|
};
|
|
console.log('Processed overviewData:', overviewData.value);
|
|
console.log('AP Connected Count:', overviewData.value.connectedApNum);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch overview data:', error);
|
|
}
|
|
};
|
|
|
|
onMounted(() => {
|
|
fetchOverviewData();
|
|
});
|
|
|
|
const deviceStatus = computed(() => {
|
|
const status = {
|
|
ap: {
|
|
connected: Number(overviewData.value.connectedApNum) || 0,
|
|
disconnected: Number(overviewData.value.disconnectedApNum) || 0,
|
|
isolated: Number(overviewData.value.isolatedApNum) || 0
|
|
},
|
|
client: {
|
|
wiredUsers: Number(overviewData.value.wiredClientNum) || 0,
|
|
wirelessUsers: Number(overviewData.value.wirelessClientNum) || 0,
|
|
wirelessGuests: Number(overviewData.value.guestNum) || 0
|
|
},
|
|
users: {
|
|
registered: Number(overviewData.value.registerUserNum) || 0,
|
|
online: Number(overviewData.value.onlineUserNum) || 0
|
|
},
|
|
alerts: 0
|
|
};
|
|
console.log('Computed deviceStatus:', status);
|
|
return status;
|
|
});
|
|
|
|
const siteInfo = computed(() => ({
|
|
total: Number(overviewData.value.siteNum) || 0
|
|
}));
|
|
|
|
const otherDevices = computed(() => ({
|
|
gateways: {
|
|
total: Number(overviewData.value.totalGatewayNum) || 0,
|
|
connected: Number(overviewData.value.connectedGatewayNum) || 0,
|
|
disconnected: Number(overviewData.value.disconnectedGatewayNum) || 0
|
|
},
|
|
switches: {
|
|
total: Number(overviewData.value.totalSwitchNum) || 0,
|
|
connected: Number(overviewData.value.connectedSwitchNum) || 0,
|
|
disconnected: Number(overviewData.value.disconnectedSwitchNum) || 0
|
|
},
|
|
olts: {
|
|
total: 0,
|
|
connected: 0,
|
|
disconnected: 0
|
|
}
|
|
}));
|
|
</script>
|
|
|
|
<template>
|
|
<ACard :bordered="false" class="card-wrapper">
|
|
<div class="text-16px font-bold mb-4px">Controller Overview</div>
|
|
<ARow :gutter="[16, 16]">
|
|
<ACol :span="24">
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-8px">
|
|
<!-- Connected Status -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-blue-50 rounded-lg">
|
|
<icon-mdi-cloud class="text-primary text-24px" />
|
|
</div>
|
|
<div class="text-16px font-medium">Connected</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="text-12px text-gray-500">
|
|
Cloud Access
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sites -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-gray-100 rounded-lg relative">
|
|
<environment-outlined class="text-24px text-primary" />
|
|
<span class="text-20px font-semibold absolute -right-2 -top-2 bg-primary text-white rounded-full w-6 h-6 flex-center">{{ siteInfo.total }}</span>
|
|
</div>
|
|
<div class="text-16px font-medium">Sites</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="flex flex-col text-12px text-gray-500">
|
|
<span>Total Sites: {{ siteInfo.total }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Gateways -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-gray-100 rounded-lg relative">
|
|
<safety-outlined class="text-24px text-primary" />
|
|
<span class="text-20px font-semibold absolute -right-2 -top-2 bg-primary text-white rounded-full w-6 h-6 flex-center">{{ otherDevices.gateways.connected + otherDevices.gateways.disconnected }}</span>
|
|
</div>
|
|
<div class="text-16px font-medium">Gateways</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="flex flex-col text-12px text-gray-500">
|
|
<span>Connected: {{ otherDevices.gateways.connected }}</span>
|
|
<span>Disconnected: {{ otherDevices.gateways.disconnected }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Switches -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-gray-100 rounded-lg relative">
|
|
<hdd-outlined class="text-24px text-primary" />
|
|
<span class="text-20px font-semibold absolute -right-2 -top-2 bg-primary text-white rounded-full w-6 h-6 flex-center">{{ otherDevices.switches.connected + otherDevices.switches.disconnected }}</span>
|
|
</div>
|
|
<div class="text-16px font-medium">Switches</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="flex flex-col text-12px text-gray-500">
|
|
<span>Connected: {{ otherDevices.switches.connected }}</span>
|
|
<span>Disconnected: {{ otherDevices.switches.disconnected }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- OLTs -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-gray-100 rounded-lg relative">
|
|
<group-outlined class="text-24px text-primary" />
|
|
<span class="text-20px font-semibold absolute -right-2 -top-2 bg-primary text-white rounded-full w-6 h-6 flex-center">{{ otherDevices.olts.total }}</span>
|
|
</div>
|
|
<div class="text-16px font-medium">OLTs</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="flex flex-col text-12px text-gray-500">
|
|
<span>Connected: {{ otherDevices.olts.connected }}</span>
|
|
<span>Disconnected: {{ otherDevices.olts.disconnected }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Register Users -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-blue-50 rounded-lg">
|
|
<user-add-outlined class="text-primary text-24px" />
|
|
</div>
|
|
<div class="text-16px font-medium">Register Users</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="flex flex-col text-12px text-gray-500">
|
|
<span>Register: {{ deviceStatus.users.registered }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Online Users -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-green-50 rounded-lg">
|
|
<user-switch-outlined class="text-primary text-24px" />
|
|
</div>
|
|
<div class="text-16px font-medium">Online Users</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="flex flex-col text-12px text-gray-500">
|
|
<span>Online: {{ deviceStatus.users.online }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- AP Status -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-green-50 rounded-lg relative">
|
|
<icon-mdi-access-point class="text-primary text-24px" />
|
|
<span class="text-20px font-semibold absolute -right-2 -top-2 bg-primary text-white rounded-full w-6 h-6 flex-center">
|
|
{{ overviewData.totalApNum }}
|
|
</span>
|
|
</div>
|
|
<div class="text-16px font-medium">AP</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="flex flex-col text-12px text-gray-500">
|
|
<span>Connected: {{ deviceStatus.ap.connected }}</span>
|
|
<span>Disconnected: {{ deviceStatus.ap.disconnected }}</span>
|
|
<span>Isolated: {{ deviceStatus.ap.isolated }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Client Status -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-purple-50 rounded-lg relative">
|
|
<icon-mdi-laptop class="text-primary text-24px" />
|
|
<span class="text-20px font-semibold absolute -right-2 -top-2 bg-primary text-white rounded-full w-6 h-6 flex-center">
|
|
{{ overviewData.totalClientNum }}
|
|
</span>
|
|
</div>
|
|
<div class="text-16px font-medium">Client</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="flex flex-col text-12px text-gray-500">
|
|
<span>Wired client: {{ deviceStatus.client.wiredUsers }}</span>
|
|
<span>Wireless client: {{ deviceStatus.client.wirelessUsers }}</span>
|
|
<span>Wireless Guests: {{ deviceStatus.client.wirelessGuests }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Alerts -->
|
|
<div class="flex flex-col p-6px">
|
|
<div class="flex items-center gap-6px mb-6px">
|
|
<div class="size-48px flex-center bg-yellow-50 rounded-lg">
|
|
<icon-mdi-alert class="text-warning text-24px" />
|
|
</div>
|
|
<div class="text-16px font-medium">Alerts</div>
|
|
</div>
|
|
<div class="border-t border-gray-100 my-4px"></div>
|
|
<div class="flex flex-col text-12px text-gray-500">
|
|
<span>Alerts: {{ deviceStatus.alerts || 0 }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ACol>
|
|
</ARow>
|
|
</ACard>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.card-wrapper {
|
|
margin-bottom: 16px;
|
|
padding: 12px;
|
|
}
|
|
|
|
.flex-center {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.size-48px {
|
|
width: 48px;
|
|
height: 48px;
|
|
position: relative;
|
|
}
|
|
|
|
.flex-col > span {
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.bg-gray-50 {
|
|
background-color: #fafafa;
|
|
}
|
|
|
|
.p-6px {
|
|
padding: 6px;
|
|
}
|
|
|
|
.mt-8px {
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.mb-6px {
|
|
margin-bottom: 6px;
|
|
}
|
|
</style>
|