chore
This commit is contained in:
54
src/components/station-card.vue
Normal file
54
src/components/station-card.vue
Normal 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>
|
||||
33
src/composables/query/use-station-list-query.ts
Normal file
33
src/composables/query/use-station-list-query.ts
Normal 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
7
src/pages/alarm-page.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div>alarm</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
31
src/pages/dashboard-page.vue
Normal file
31
src/pages/dashboard-page.vue
Normal 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>
|
||||
7
src/pages/device-page.vue
Normal file
7
src/pages/device-page.vue
Normal 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
7
src/pages/log-page.vue
Normal 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
101
src/pages/login-page.vue
Normal 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>
|
||||
20
src/pages/not-found-page.vue
Normal file
20
src/pages/not-found-page.vue
Normal 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>
|
||||
7
src/pages/statistics-page.vue
Normal file
7
src/pages/statistics-page.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div>statistics</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user