review:【antd】【iot】首页的迁移
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
/**
|
||||
* 消息趋势图表配置
|
||||
*/
|
||||
/** 消息趋势图表配置 */
|
||||
export function getMessageTrendChartOptions(
|
||||
times: string[],
|
||||
upstreamData: number[],
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
/**
|
||||
* IoT 首页数据配置文件
|
||||
*
|
||||
* 该文件只包含统计数据接口定义
|
||||
*/
|
||||
|
||||
import type { IotStatisticsApi } from '#/api/iot/statistics';
|
||||
|
||||
/** 统计数据接口 - 使用 API 定义的类型 */
|
||||
export type StatsData = IotStatisticsApi.StatisticsSummary;
|
||||
|
||||
/** 默认统计数据 */
|
||||
export const defaultStatsData: StatsData = {
|
||||
export const defaultStatsData: IotStatisticsApi.StatisticsSummary = {
|
||||
productCategoryCount: 0,
|
||||
productCount: 0,
|
||||
deviceCount: 0,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { ComparisonCard, Page } from '@vben/common-ui';
|
||||
import {ComparisonCard, Page} from '@vben/common-ui';
|
||||
|
||||
import { Col, Row } from 'ant-design-vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import {Col, Row} from 'ant-design-vue';
|
||||
import {onMounted, ref} from 'vue';
|
||||
|
||||
import { getStatisticsSummary } from '#/api/iot/statistics';
|
||||
import {getStatisticsSummary} from '#/api/iot/statistics';
|
||||
|
||||
import { defaultStatsData, type StatsData } from './data';
|
||||
import {defaultStatsData, type StatsData} from './data';
|
||||
import DeviceCountCard from './modules/device-count-card.vue';
|
||||
import DeviceStateCountCard from './modules/device-state-count-card.vue';
|
||||
import MessageTrendCard from './modules/message-trend-card.vue';
|
||||
@@ -16,20 +16,18 @@ defineOptions({ name: 'IoTHome' });
|
||||
const loading = ref(true);
|
||||
const statsData = ref<StatsData>(defaultStatsData);
|
||||
|
||||
/**
|
||||
* 加载统计数据
|
||||
* @returns Promise<StatsData>
|
||||
*/
|
||||
/** 加载统计数据 */
|
||||
async function loadStatisticsData(): Promise<StatsData> {
|
||||
try {
|
||||
const data = await getStatisticsSummary();
|
||||
return data;
|
||||
return await getStatisticsSummary();
|
||||
} catch (error) {
|
||||
// TODO @haohao:后续记得删除下哈。catch 部分可以删除
|
||||
// 开发环境:记录错误信息,便于调试
|
||||
console.error('获取统计数据出错:', error);
|
||||
// 开发环境:提示使用 Mock 数据,提醒检查后端接口
|
||||
console.warn('使用 Mock 数据,请检查后端接口是否已实现');
|
||||
|
||||
// TODO @haohao:后续记得删除下哈。
|
||||
// 开发调试:返回 Mock 数据,确保前端功能正常开发
|
||||
// 生产环境:建议移除 Mock 数据,直接抛出错误或返回空数据
|
||||
return {
|
||||
@@ -54,15 +52,13 @@ async function loadStatisticsData(): Promise<StatsData> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
/** 加载数据 */
|
||||
async function loadData() {
|
||||
loading.value = true;
|
||||
try {
|
||||
statsData.value = await loadStatisticsData();
|
||||
} catch (error) {
|
||||
// 开发环境:记录错误信息,便于调试
|
||||
// TODO @haohao:后续记得删除下哈。catch 部分可以删除
|
||||
console.error('获取统计数据出错:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
|
||||
@@ -35,11 +35,10 @@ async function initChart() {
|
||||
}
|
||||
|
||||
await nextTick();
|
||||
const data = Object.entries(
|
||||
props.statsData.productCategoryDeviceCounts,
|
||||
).map(([name, value]) => ({ name, value }));
|
||||
|
||||
renderEcharts(getDeviceCountPieChartOptions(data));
|
||||
const data = Object.entries(props.statsData.productCategoryDeviceCounts).map(
|
||||
([name, value]) => ({ name, value }),
|
||||
);
|
||||
await renderEcharts(getDeviceCountPieChartOptions(data));
|
||||
}
|
||||
|
||||
/** 监听数据变化 */
|
||||
|
||||
@@ -41,7 +41,7 @@ async function initCharts() {
|
||||
await nextTick();
|
||||
const max = props.statsData.deviceCount || 100;
|
||||
// 在线设备
|
||||
renderOnlineChart(
|
||||
await renderOnlineChart(
|
||||
getDeviceStateGaugeChartOptions(
|
||||
props.statsData.deviceOnlineCount,
|
||||
max,
|
||||
@@ -50,7 +50,7 @@ async function initCharts() {
|
||||
),
|
||||
);
|
||||
// 离线设备
|
||||
renderOfflineChart(
|
||||
await renderOfflineChart(
|
||||
getDeviceStateGaugeChartOptions(
|
||||
props.statsData.deviceOfflineCount,
|
||||
max,
|
||||
@@ -59,7 +59,7 @@ async function initCharts() {
|
||||
),
|
||||
);
|
||||
// 待激活设备
|
||||
renderInactiveChart(
|
||||
await renderInactiveChart(
|
||||
getDeviceStateGaugeChartOptions(
|
||||
props.statsData.deviceInactiveCount,
|
||||
max,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { Dayjs } from 'dayjs';
|
||||
|
||||
import type { IotStatisticsApi } from '#/api/iot/statistics';
|
||||
|
||||
import { computed, nextTick, onMounted, reactive, ref } from 'vue';
|
||||
@@ -11,8 +12,8 @@ import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
|
||||
import { Card, Empty, Select } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import ShortcutDateRangePicker from '#/components/shortcut-date-range-picker/shortcut-date-range-picker.vue';
|
||||
import { getDeviceMessageSummaryByDate } from '#/api/iot/statistics';
|
||||
import ShortcutDateRangePicker from '#/components/shortcut-date-range-picker/shortcut-date-range-picker.vue';
|
||||
|
||||
import { getMessageTrendChartOptions } from '../chart-options';
|
||||
|
||||
@@ -31,16 +32,9 @@ const dateRange = ref<[string, string]>([
|
||||
dayjs().format('YYYY-MM-DD'),
|
||||
]);
|
||||
|
||||
/**
|
||||
* 将日期范围转换为带时分秒的格式
|
||||
* @param dates 日期范围 [开始日期, 结束日期],格式为 YYYY-MM-DD
|
||||
* @returns 带时分秒的日期范围 [开始日期 00:00:00, 结束日期 23:59:59]
|
||||
*/
|
||||
/** 将日期范围转换为带时分秒的格式 */
|
||||
function formatDateRangeWithTime(dates: [string, string]): [string, string] {
|
||||
return [
|
||||
`${dates[0]} 00:00:00`,
|
||||
`${dates[1]} 23:59:59`,
|
||||
];
|
||||
return [`${dates[0]} 00:00:00`, `${dates[1]} 23:59:59`];
|
||||
}
|
||||
|
||||
/** 查询参数 */
|
||||
@@ -69,7 +63,9 @@ function handleQuery() {
|
||||
|
||||
/** 处理时间范围变化 */
|
||||
function handleDateRangeChange(times?: [Dayjs, Dayjs]) {
|
||||
if (!times || times.length !== 2) return;
|
||||
if (!times || times.length !== 2) {
|
||||
return;
|
||||
}
|
||||
dateRange.value = [
|
||||
dayjs(times[0]).format('YYYY-MM-DD'),
|
||||
dayjs(times[1]).format('YYYY-MM-DD'),
|
||||
@@ -86,12 +82,15 @@ function handleIntervalChange() {
|
||||
|
||||
/** 获取消息统计数据 */
|
||||
async function fetchMessageData() {
|
||||
if (!queryParams.times || queryParams.times.length !== 2) return;
|
||||
if (!queryParams.times || queryParams.times.length !== 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
messageData.value = await getDeviceMessageSummaryByDate(queryParams);
|
||||
} catch (error) {
|
||||
// TODO @haohao:catch 可以删除哈;
|
||||
// 开发环境:记录错误信息,便于调试
|
||||
console.error('获取消息统计数据失败:', error);
|
||||
// 错误时清空数据,避免显示错误的数据
|
||||
@@ -105,12 +104,13 @@ async function fetchMessageData() {
|
||||
/** 初始化图表 */
|
||||
function initChart() {
|
||||
// 检查数据是否存在
|
||||
if (!hasData.value) return;
|
||||
if (!hasData.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const times = messageData.value.map((item) => item.time);
|
||||
const upstreamData = messageData.value.map((item) => item.upstreamCount);
|
||||
const downstreamData = messageData.value.map((item) => item.downstreamCount);
|
||||
|
||||
renderEcharts(
|
||||
getMessageTrendChartOptions(times, upstreamData, downstreamData),
|
||||
);
|
||||
@@ -118,7 +118,9 @@ function initChart() {
|
||||
|
||||
/** 确保图表容器已经可见后再渲染 */
|
||||
async function renderChartWhenReady() {
|
||||
if (!hasData.value) return;
|
||||
if (!hasData.value) {
|
||||
return;
|
||||
}
|
||||
// 等待 Card loading 状态、v-show 等 DOM 更新完成
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
@@ -138,7 +140,9 @@ onMounted(() => {
|
||||
<span class="text-base font-medium text-gray-600">消息量统计</span>
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-sm text-gray-500 whitespace-nowrap">时间范围</span>
|
||||
<span class="whitespace-nowrap text-sm text-gray-500"
|
||||
>时间范围</span
|
||||
>
|
||||
<ShortcutDateRangePicker @change="handleDateRangeChange" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -174,8 +178,4 @@ onMounted(() => {
|
||||
<EchartsUI ref="messageChartRef" class="h-[300px] w-full" />
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
</template>
|
||||
@@ -16,6 +16,7 @@ defineOptions({ name: 'ComparisonCard' });
|
||||
|
||||
const props = defineProps<ComparisonCardProps>();
|
||||
|
||||
// TODO @haohao:看看能不能用中立的 icon,类似 ADD、EDIT 那种。目的:方便后续迁移到 ele 版本里。
|
||||
const iconMap: Record<string, string> = {
|
||||
menu: 'ant-design:appstore-outlined',
|
||||
box: 'ant-design:box-plot-outlined',
|
||||
@@ -39,11 +40,7 @@ const iconName = computed(() => iconMap[props.icon] || iconMap.menu);
|
||||
</span>
|
||||
<span class="text-3xl font-bold text-gray-800">
|
||||
<span v-if="value === -1">--</span>
|
||||
<VbenCountToAnimator
|
||||
v-else
|
||||
:end-val="value"
|
||||
:duration="1000"
|
||||
/>
|
||||
<VbenCountToAnimator v-else :end-val="value" :duration="1000" />
|
||||
</span>
|
||||
</div>
|
||||
<div :class="`text-4xl ${iconColor || ''}`">
|
||||
@@ -63,4 +60,3 @@ const iconName = computed(() => iconMap[props.icon] || iconMap.menu);
|
||||
</CardContent>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// add by 芋艿:对比卡片,目前 iot 模块在使用
|
||||
export { default as ComparisonCard } from './comparison-card.vue';
|
||||
export * from './types';
|
||||
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
export interface ComparisonCardProps {
|
||||
/** 图标名称 */
|
||||
icon: string;
|
||||
/** 图标颜色类名 */
|
||||
iconColor?: string;
|
||||
/** 加载状态 */
|
||||
loading?: boolean;
|
||||
/** 标题 */
|
||||
title: string;
|
||||
/** 今日新增数量 */
|
||||
todayCount: number;
|
||||
/** 数值 */
|
||||
value: number;
|
||||
icon: string; // 图标名称
|
||||
iconColor?: string; // 图标颜色类名
|
||||
loading?: boolean; // 加载状态
|
||||
title: string; // 标题
|
||||
todayCount: number; // 今日新增数量
|
||||
value: number; // 数值
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user