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