review:【antd】【iot】首页的迁移

This commit is contained in:
YunaiV
2025-11-23 18:51:16 +08:00
parent a32ff4a9c9
commit 42697ec614
9 changed files with 49 additions and 77 deletions

View File

@@ -1,6 +1,4 @@
/**
* 消息趋势图表配置
*/
/** 消息趋势图表配置 */
export function getMessageTrendChartOptions(
times: string[],
upstreamData: number[],

View File

@@ -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,

View File

@@ -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;

View File

@@ -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));
}
/** 监听数据变化 */

View File

@@ -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,

View File

@@ -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 @haohaocatch 可以删除哈;
// 开发环境:记录错误信息,便于调试
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>

View File

@@ -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>

View File

@@ -1,4 +1,3 @@
// add by 芋艿:对比卡片,目前 iot 模块在使用
export { default as ComparisonCard } from './comparison-card.vue';
export * from './types';

View File

@@ -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; // 数值
}