This commit is contained in:
yangsy
2025-08-14 13:06:22 +08:00
parent a1a73180b4
commit dd317df58a
9 changed files with 267 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
<script setup lang="ts">
import { NCard, NStatistic, NTag, NIcon, NGrid, NGi } from 'naive-ui';
import { Wifi } from '@vicons/ionicons5';
import { toRefs } from 'vue';
// 定义组件props
interface Props {
name: string;
online: boolean;
offlineDeviceCount: number;
alarmCount: number;
}
const props = defineProps<Props>();
const { name, online, offlineDeviceCount, alarmCount } = toRefs(props);
</script>
<template>
<NCard bordered hoverable size="small" :title="name" class="station-card">
<template #header-extra>
<NTag :type="online ? 'success' : 'error'" size="small">
<template #icon>
<NIcon><Wifi /></NIcon>
</template>
{{ online ? '在线' : '离线' }}
</NTag>
</template>
<template #default>
<NGrid :cols="2">
<NGi>
<NStatistic label="离线设备" :value="offlineDeviceCount" :tabular-nums="true">
<template #suffix>
<span class="stat-suffix"></span>
</template>
</NStatistic>
</NGi>
<NGi>
<NStatistic label="告警记录" :value="alarmCount" :tabular-nums="true">
<template #suffix>
<span class="stat-suffix"></span>
</template>
</NStatistic>
</NGi>
</NGrid>
</template>
</NCard>
</template>
<style scoped lang="scss">
.stat-suffix {
font-size: 14px;
}
</style>

View File

@@ -0,0 +1,33 @@
import type { Station } from '@/apis/domains';
import { ndmVerify } from '@/apis/requests';
import { useQuery } from '@tanstack/vue-query';
import { useStationStore } from '@/stores/station';
import axios from 'axios';
import dayjs from 'dayjs';
import { storeToRefs } from 'pinia';
export function useStationListQuery() {
const stationStore = useStationStore();
const { stationList } = storeToRefs(stationStore);
useQuery({
queryKey: ['station-list'],
queryFn: async () => {
const { data: ndmStationList } = await axios.get<{ code: string; name: string }[]>(`/minio/ndm/ndm-stations.json?_t=${dayjs().unix()}`);
stationList.value = ndmStationList.map<Station>((record) => ({
code: record.code ?? '',
name: record.name ?? '',
online: false,
}));
const pingResultList = await Promise.allSettled(stationList.value.map((station) => ndmVerify(station.code)));
stationList.value = stationList.value.map((station, index) => ({
...station,
online: pingResultList[index].status === 'fulfilled',
}));
return stationList.value;
},
});
}

7
src/pages/alarm-page.vue Normal file
View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>alarm</div>
</template>
<style scoped></style>

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
import { ref } from 'vue';
import StationCard from '@/components/station-card.vue';
import { NGrid, NGi } from 'naive-ui';
import { useQuery } from '@tanstack/vue-query';
// 模拟数据
const stations = ref(
Array.from({ length: 32 }).map((_, i) => ({
name: `车站 ${i + 1}`,
online: Math.random() > 0.5,
offlineDeviceCount: Math.floor(Math.random() * 20),
alarmCount: Math.floor(Math.random() * 10),
})),
);
// const query = useQuery({
// queryKey: ['line-devices'],
// queryFn: async () => {},
// });
</script>
<template>
<NGrid :cols="8" :x-gap="12" :y-gap="12">
<NGi v-for="station in stations" :key="station.name">
<StationCard :name="station.name" :online="station.online" :offline-device-count="station.offlineDeviceCount" :alarm-count="station.alarmCount" />
</NGi>
</NGrid>
</template>
<style scoped></style>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>device</div>
</template>
<style scoped></style>

7
src/pages/log-page.vue Normal file
View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>log</div>
</template>
<style scoped></style>

101
src/pages/login-page.vue Normal file
View File

@@ -0,0 +1,101 @@
<script setup lang="ts">
import { reactive } from 'vue';
import { NLayout, NCard, NForm, NFormItem, NInput, NButton, NFlex } from 'naive-ui';
import ThemeSwitch from '@/components/theme-switch.vue';
import { useUserStore } from '@/stores/user';
import type { LoginParams } from '@/apis/models/user';
import { randomNum } from '@/utils/random-num';
import { userClient } from '@/apis/client';
import { useRouter } from 'vue-router';
const router = useRouter();
import { useMutation } from '@tanstack/vue-query';
const formData = reactive<LoginParams>({
username: '',
password: '',
code: '',
key: randomNum(24, 16),
grantType: 'PASSWORD',
});
const { mutate: login, isPending: loading } = useMutation({
mutationFn: async (params: LoginParams) => {
const userStore = useUserStore();
await userStore.userLogin(params);
const [err] = await userClient.post<void>(`/api/ndm/ndmKeepAlive/verify`, {}, { timeout: 5000 });
if (err) throw err;
},
onSuccess: () => {
window.$message.success('登录成功');
router.push('/');
},
});
</script>
<template>
<NLayout position="absolute" class="login-page">
<div class="login-card-container">
<NCard class="login-card">
<ThemeSwitch class="theme-switch" />
<NFlex vertical justify="center" align="center">
<span class="platform-title">网络设备管理平台</span>
<span class="login-title">登录</span>
<NForm :model="formData" label-placement="top" class="login-form">
<NFormItem label="用户名" path="username">
<NInput v-model:value="formData.username" placeholder="请输入用户名" />
</NFormItem>
<NFormItem label="密码" path="password">
<NInput v-model:value="formData.password" type="password" show-password-on="click" placeholder="请输入密码" />
</NFormItem>
</NForm>
<NButton type="primary" block strong :loading="loading" @click="() => login(formData)"> 登录 </NButton>
</NFlex>
</NCard>
</div>
</NLayout>
</template>
<style scoped lang="scss">
.login-page {
}
.login-card-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.login-card {
width: 400px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 32px;
position: relative;
}
.theme-switch {
position: absolute;
bottom: 16px;
right: 16px;
}
.platform-title {
font-size: 28px;
font-weight: 500;
margin-bottom: 8px;
}
.login-title {
font-size: 16px;
color: #999;
margin-bottom: 24px;
}
.login-form {
width: 100%;
margin-bottom: 16px;
}
</style>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import { NResult, NButton } from 'naive-ui';
import { useRouter } from 'vue-router';
const router = useRouter();
const goHome = () => {
router.push('/');
};
</script>
<template>
<NResult status="404" title="404 页面不存在" style="margin-top: 50px">
<template #footer>
<NButton @click="goHome">返回</NButton>
</template>
</NResult>
</template>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>statistics</div>
</template>
<style scoped></style>