Softhub软件下载站实战开发(十六):仪表盘前端设计与实现
文章目录
- Softhub软件下载站实战开发(十六):仪表盘前端设计与实现 🎛️
- 前言
- 主要功能点 ✨
- 技术实现 🛠️
- 1. 接口设计与类型定义
- 2. API请求封装
- 3. 核心组件实现
- 数据统计卡片 📊
- 最新软件与待办事项 📋
- 存储空间分布 💾
- 4. 数据处理逻辑
Softhub软件下载站实战开发(十六):仪表盘前端设计与实现 🎛️
前言
在Softhub软件下载站的管理后台中,仪表盘(Dashboard) 是管理员查看系统运行状态的核心界面。本文将详细介绍我们如何基于Vue3+Element Plus实现一个功能丰富、数据可视化的管理仪表盘。
效果展示
主要功能点 ✨
- 多维度数据统计:软件总数、分类数、平台数等核心指标一目了然
- 实时数据展示:最新软件、待处理事项等动态信息
- 存储空间可视化:直观展示各类别软件占用空间比例
技术实现 🛠️
1. 接口设计与类型定义
我们首先在types.ts
中定义了严谨的TypeScript类型:
// Dashboard API 类型定义// 基础统计数据
export interface DashboardBasicStats {software: {total: number;monthlyNew: number;};category: {total: number;};platform: {total: number;};resource: {total: number;monthlyNew: number;totalSize: string;};downloads: {total: number;};
}// 分类分布数据项
export interface CategoryDistributionItem {categoryName: string;count: number;percentage: number;
}// 平台分布数据项
export interface PlatformDistributionItem {platformName: string;count: number;percentage: number;
}// 最新软件列表数据
export interface LatestSoftwareList {list: LatestSoftwareItem[];
}// 最新软件数据项
export interface LatestSoftwareItem {id: string;name: string;categoryName: string;platformName: string;createTime: string;
}// 待处理事项数据
export interface TodoItems {list: TodoItem[];
}export interface TodoItem {id: string;name: string;categoryName: string;platformName: string;type: string; // 问题类型:noCategory, noResource, noCovertypeText: string; // 问题类型文本createTime: string;
}// 存储空间分布数据项
export interface StorageDistributionItem {categoryName: string;size: string;percentage: number;
}// 存储空间列表数据
export interface StorageDistributionList {list: StorageDistributionItem[];
}// API 响应类型
export interface ApiResponse<T> {code: number;message: string;data: T;
}// Dashboard API 响应类型
export type BasicStatsResponse = ApiResponse<DashboardBasicStats>;
export type CategoryDistributionResponse = ApiResponse<CategoryDistributionItem[]>;
export type PlatformDistributionResponse = ApiResponse<PlatformDistributionItem[]>;
export type LatestSoftwareResponse = ApiResponse<LatestSoftwareList>;
export type TodoItemsResponse = ApiResponse<TodoItems>;
export type StorageDistributionResponse = ApiResponse<StorageDistributionList>;
2. API请求封装
在index.ts
中封装了所有仪表盘相关的API请求:
import request from '/@/utils/request';
import type {BasicStatsResponse,CategoryDistributionResponse,PlatformDistributionResponse,LatestSoftwareResponse,TodoItemsResponse,StorageDistributionResponse,DashboardBasicStats,CategoryDistributionItem,PlatformDistributionItem,LatestSoftwareItem,TodoItems,StorageDistributionItem
} from './types';// 获取基础统计数据
export function getBasicStats(): Promise<BasicStatsResponse> {return request({url: '/api/v1/admin/ds/dashboard/basic-stats',method: 'get'})
}// 获取分类分布数据
export function getCategoryDistribution(): Promise<CategoryDistributionResponse> {return request({url: '/api/v1/admin/ds/dashboard/category-distribution',method: 'get'})
}// 获取平台分布数据
export function getPlatformDistribution(): Promise<PlatformDistributionResponse> {return request({url: '/api/v1/admin/ds/dashboard/platform-distribution',method: 'get'})
}// 获取最新软件数据
export function getLatestSoftware(limit?: number): Promise<LatestSoftwareResponse> {return request({url: '/api/v1/admin/ds/dashboard/latest-software',method: 'get',params: { limit }})
}// 获取待处理事项数据
export function getTodoItems(): Promise<TodoItemsResponse> {return request({url: '/api/v1/admin/ds/dashboard/todo-items',method: 'get'})
}// 获取存储空间分布数据
export function getStorageDistribution(): Promise<StorageDistributionResponse> {return request({url: '/api/v1/admin/ds/dashboard/storage-distribution',method: 'get'})
}// 导出类型
export type {DashboardBasicStats,CategoryDistributionItem,PlatformDistributionItem,LatestSoftwareItem,TodoItems,StorageDistributionItem,BasicStatsResponse,CategoryDistributionResponse,PlatformDistributionResponse,LatestSoftwareResponse,TodoItemsResponse,StorageDistributionResponse
};
3. 核心组件实现
仪表盘主要包含三大区域:
数据统计卡片 📊
<!-- 数据统计卡片 --><el-row :gutter="20" class="stats-row"><el-col :xs="24" :sm="12" :md="4" :lg="4" :xl="4" v-for="(item, index) in statsData" :key="index"><div class="stats-card" :class="`stats-card-${index}`"><div class="stats-content"><div class="stats-icon"><component :is="item.icon" /></div><div class="stats-info"><div class="stats-number">{{ item.value }}</div><div class="stats-label">{{ item.label }}</div><div v-if="item.subValue" class="stats-sub-value">{{ item.subValue }}</div></div></div></div></el-col></el-row>
最新软件与待办事项 📋
采用左右布局展示最新上传的软件和待处理事项:
<!-- 最新软件列表 --><el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12"><div class="content-card"><div class="card-header"><h3>最新软件</h3><el-button type="primary" size="small" @click="goToSoftware">查看更多</el-button></div><div class="card-content"><div v-if="latestSoftware.length === 0" class="empty-state"><i class="el-icon-folder-opened"></i><p>暂无软件数据</p></div><div v-else class="software-list"><div v-for="software in latestSoftware" :key="software.id" class="software-item"><div class="software-info"><div class="software-name">{{ software.name }}</div><div class="software-meta"><span class="category">{{ software.categoryName }}</span><span class="platform">{{ software.platformName }}</span></div></div><div class="software-actions"><el-button link size="small" @click="viewSoftware(software.id)">查看</el-button></div></div></div></div></div></el-col>
存储空间分布 💾
<el-row :gutter="20" class="storage-row"><el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24"><div class="content-card"><div class="card-header"><h3>存储空间分布</h3></div><div class="card-content"><div v-if="storageData.breakdown.length === 0" class="empty-state"><i class="el-icon-folder-opened"></i><p>暂无存储空间数据</p></div><div v-else class="storage-overview"><div class="storage-total"><div class="storage-used"><span class="used-size">总使用空间: {{ formatFileSize(storageData.used) }}</span></div></div><div class="storage-breakdown"><div v-for="item in storageData.breakdown" :key="item.name" class="storage-item"><div class="storage-item-info"><div class="storage-item-name">{{ item.name }}</div><div class="storage-item-size">{{ formatFileSize(item.size) }}</div></div><div class="storage-item-bar"><div class="storage-item-progress" :style="{ width: item.percentage + '%', backgroundColor: item.color }"></div></div></div></div></div></div></div></el-col></el-row></div>
</template>
4. 数据处理逻辑
在组件中实现了数据获取与转换逻辑:
// 获取仪表板数据const fetchDashboardData = async () => {try {// 获取基础统计数据console.log('开始获取基础统计数据...');const basicStatsResponse = await getBasicStats();console.log('基础统计数据响应:', basicStatsResponse);if (basicStatsResponse.code === 0) {const data = basicStatsResponse.data;console.log('基础统计数据:', data);// 更新统计数据statsData.value[0].value = data.software.total || 0;statsData.value[0].subValue = `本月新增: ${data.software.monthlyNew || 0}`;statsData.value[1].value = data.category.total || 0;statsData.value[2].value = data.platform.total || 0;statsData.value[3].value = data.resource.total || 0;statsData.value[3].subValue = `本月新增: ${data.resource.monthlyNew || 0}`;statsData.value[4].value = data.downloads.total || 0;console.log('更新后的统计数据:', statsData.value);console.log('✅ 基础统计数据获取成功!');} else {console.error('❌ 基础统计数据接口返回错误:', basicStatsResponse.message);}// 获取最新软件列表console.log('开始获取最新软件列表...');const latestSoftwareResponse = await getLatestSoftware(10);console.log('最新软件列表响应:', latestSoftwareResponse);if (latestSoftwareResponse.code === 0) {latestSoftware.value = latestSoftwareResponse.data.list || [];console.log('最新软件列表数据:', latestSoftware.value);console.log('✅ 最新软件列表获取成功!');} else {console.error('❌ 最新软件列表接口返回错误:', latestSoftwareResponse.message);}// 获取待处理事项console.log('开始获取待处理事项...');const todoItemsResponse = await getTodoItems();console.log('待处理事项响应:', todoItemsResponse);if (todoItemsResponse.code === 0) {tasksData.value = todoItemsResponse.data.list || [];console.log('待处理事项数据:', tasksData.value);console.log('✅ 待处理事项获取成功!');} else {console.error('❌ 待处理事项接口返回错误:', todoItemsResponse.message);}// 获取存储空间数据console.log('开始获取存储空间数据...');const storageResponse = await getStorageDistribution();console.log('存储空间响应:', storageResponse);if (storageResponse.code === 0) {const storageItems = storageResponse.data.list;console.log('存储空间原始数据:', storageItems);// 计算总存储空间let totalSize = 0;storageItems.forEach(item => {// 解析size字段,支持GB和MB单位const sizeStr = item.size;let sizeInBytes = 0;if (sizeStr.includes('GB')) {sizeInBytes = parseFloat(sizeStr.replace('GB', '')) * 1024 * 1024 * 1024;} else if (sizeStr.includes('MB')) {sizeInBytes = parseFloat(sizeStr.replace('MB', '')) * 1024 * 1024;} else {sizeInBytes = parseFloat(sizeStr) * 1024 * 1024 * 1024; // 默认GB}totalSize += sizeInBytes;});storageData.used = totalSize;storageData.total = totalSize; // 总容量等于已使用空间storageData.percentage = 100; // 显示100%// 转换存储空间分布数据storageData.breakdown = storageItems.map(item => {// 解析size字段const sizeStr = item.size;let sizeInBytes = 0;if (sizeStr.includes('GB')) {sizeInBytes = parseFloat(sizeStr.replace('GB', '')) * 1024 * 1024 * 1024;} else if (sizeStr.includes('MB')) {sizeInBytes = parseFloat(sizeStr.replace('MB', '')) * 1024 * 1024;} else {sizeInBytes = parseFloat(sizeStr) * 1024 * 1024 * 1024; // 默认GB}return {name: item.categoryName,size: sizeInBytes,percentage: item.percentage,color: getRandomColor()};});console.log('处理后的存储空间数据:', storageData);console.log('✅ 存储空间数据获取成功!');} else {console.error('❌ 存储空间接口返回错误:', storageResponse.message);}} catch (error) {console.error('获取仪表板数据失败:', error);}};
格式化大小
const formatFileSize = (bytes: number): string => {if (bytes === 0) return '0 B';const k = 1024;const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];const i = Math.floor(Math.log(bytes) / Math.log(k));return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];};
获取任务类型
// 获取任务类型类
const getTaskTypeClass = (type: string): string => {switch (type) {case 'noCategory':return 'task-type-urgent'; // 未分类 - 高优先级case 'noResource':return 'task-type-urgent'; // 缺少资源 - 高优先级case 'noCover':return 'task-type-low'; // 缺少封面 - 低优先级case 'multiple':return 'task-type-urgent'; // 多个问题 - 高优先级case 'urgent':return 'task-type-urgent';case 'normal':return 'task-type-normal';case 'low':return 'task-type-low';default:return 'task-type-normal';}
};
softhub系列往期文章
- Softhub软件下载站实战开发(一):项目总览
- Softhub软件下载站实战开发(二):项目基础框架搭建
- Softhub软件下载站实战开发(三):平台管理模块实战
- Softhub软件下载站实战开发(四):代码生成器设计与实现
- Softhub软件下载站实战开发(五):分类模块实现
- Softhub软件下载站实战开发(六):软件配置面板实现
- Softhub软件下载站实战开发(七):集成MinIO实现文件存储功能
- Softhub软件下载站实战开发(八):编写软件后台管理
- Softhub软件下载站实战开发(九):编写软件配置管理界面
- Softhub软件下载站实战开发(十):实现图片视频上传下载接口
- Softhub软件下载站实战开发(十一):软件分片上传接口实现
- Softhub软件下载站实战开发(十二):软件管理编辑页面实现
- Softhub软件下载站实战开发(十三):软件管理前端分片上传实现
- Softhub软件下载站实战开发(十四):软件收藏集设计
- Softhub软件下载站实战开发(十五):仪表盘API设计