当前位置: 首页 > news >正文

Softhub软件下载站实战开发(十六):仪表盘前端设计与实现

文章目录

  • Softhub软件下载站实战开发(十六):仪表盘前端设计与实现 🎛️
    • 前言
    • 主要功能点 ✨
    • 技术实现 🛠️
      • 1. 接口设计与类型定义
      • 2. API请求封装
      • 3. 核心组件实现
        • 数据统计卡片 📊
        • 最新软件与待办事项 📋
        • 存储空间分布 💾
      • 4. 数据处理逻辑

Softhub软件下载站实战开发(十六):仪表盘前端设计与实现 🎛️

前言

在Softhub软件下载站的管理后台中,仪表盘(Dashboard) 是管理员查看系统运行状态的核心界面。本文将详细介绍我们如何基于Vue3+Element Plus实现一个功能丰富、数据可视化的管理仪表盘。

效果展示

image.png

主要功能点 ✨

  • 多维度数据统计:软件总数、分类数、平台数等核心指标一目了然
  • 实时数据展示:最新软件、待处理事项等动态信息
  • 存储空间可视化:直观展示各类别软件占用空间比例

技术实现 🛠️

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系列往期文章

  1. Softhub软件下载站实战开发(一):项目总览
  2. Softhub软件下载站实战开发(二):项目基础框架搭建
  3. Softhub软件下载站实战开发(三):平台管理模块实战
  4. Softhub软件下载站实战开发(四):代码生成器设计与实现
  5. Softhub软件下载站实战开发(五):分类模块实现
  6. Softhub软件下载站实战开发(六):软件配置面板实现
  7. Softhub软件下载站实战开发(七):集成MinIO实现文件存储功能
  8. Softhub软件下载站实战开发(八):编写软件后台管理
  9. Softhub软件下载站实战开发(九):编写软件配置管理界面
  10. Softhub软件下载站实战开发(十):实现图片视频上传下载接口
  11. Softhub软件下载站实战开发(十一):软件分片上传接口实现
  12. Softhub软件下载站实战开发(十二):软件管理编辑页面实现
  13. Softhub软件下载站实战开发(十三):软件管理前端分片上传实现
  14. Softhub软件下载站实战开发(十四):软件收藏集设计
  15. Softhub软件下载站实战开发(十五):仪表盘API设计
http://www.xdnf.cn/news/1089595.html

相关文章:

  • 【DOCKER】-2 docker基础
  • 车载以太网-TC8测试-UT(Upper Tester)
  • 基于物联网的智能交通灯控制系统设计
  • RAGflow图像解析与向量化分析
  • 基于模板设计模式开发优惠券推送功能以及对过期优惠卷进行定时清理
  • 文献学习|全面绘制和建模水稻调控组景观揭示了复杂性状背后的调控架构。
  • 【洛谷题单】--顺序结构(一)
  • MyBatis-Plus:深入探索与最佳实践
  • 《UE5_C++多人TPS完整教程》学习笔记42 ——《P43 瞄准(Aiming)》
  • 【Linux服务器】-安装ftp与sftp服务
  • JavaScript基础篇——第二章 类型转换与常见错误解析
  • openEuler2203sp4-vg磁盘组中剔除磁盘
  • 11款常用C++在线编译与运行平台推荐与对比
  • 【PyTorch】PyTorch中torch.nn模块的激活函数
  • 初识mysql(一)
  • Spring Bean 的生命周期
  • Python入门Day5
  • 数据结构*搜索树
  • CI/CD — DevOps概念之实现k8s持续交付持续集成(一)
  • Maven 打包排除特定依赖的完整指南(详细方法 + 示例)
  • -温差双八无碳小车cad【14张】+三维图+设计说明书+总装配图+绛重
  • 高版本的MacOS如何降级?
  • Qt:图片切割
  • numpy数据分析知识总结
  • 使用Node.js搭建Web应用有哪些注意事项?
  • LocalStorage和SessionStorage的区别和应用
  • 零基础 Qt 6 在线安装教程
  • C++11 算法详解:std::copy_if 与 std::copy_n
  • 基于物联网架构的温室环境温湿度传感器节点设计
  • C++ 遍历可变参数的几种方法