当前位置: 首页 > 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.dtcms.com/a/270230.html

相关文章:

  • 香港风水(原生)林地的逻辑分类器
  • 缺乏项目进度预警机制,如何建立预警体系
  • 从零开始手写嵌入式实时操作系统
  • 【c++八股文】Day4:右值,右值引用,移动语义
  • 使用协程简化异步资源获取操作
  • qt-C++语法笔记之Stretch与Spacer的关系分析
  • Python Web应用开发之Flask框架高级应用(三)——蓝图(Blueprints)
  • openssl 生成国密证书
  • 北京-4年功能测试2年空窗-报培训班学测开-第四十五天
  • [附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+vue实现的供电公司安全生产考试管理系统,推荐!
  • 【OD机试题解法笔记】跳马
  • MySQL8.0.40.0MSI安装教程
  • [特殊字符] AlphaGo:“神之一手”背后的智能革命与人机博弈新纪元
  • 汽车功能安全系统阶段开发【技术安全方案TSC以及安全分析】5
  • TypeScript 接口全解析:从基础到高级应用
  • Crazyflie无人机集群控制笔记(一)通过VRPN实时对接Crazyswarm2与NOKOV度量动捕数据
  • 数据湖技术之Iceberg-03 Iceberg整合Flink 实时写入与增量读取
  • Linux文件描述符与标准I/O终极对比
  • BabelDOC,一个专为学术PDF文档设计的翻译和双语对比工具
  • C#使用Semantic Kernel实现Embedding功能
  • 解决GitHub仓库推送子文件夹后打不开的问题
  • C++高频知识点(六)
  • vue3使用inspira-ui教程【附带源码】
  • Ansible 介绍及安装
  • ubuntu24.04(vmware workstation 17.6pro)无法安装vmtools的问题解决
  • mini-program01の系统认识微信小程序开发
  • 云原生详解:构建现代化应用的未来
  • 【读论文】GLM-4.1V-Thinking 解读:用强化学习解锁 VLM 的通用推理能力
  • Tensor数据转换
  • 模型训练篇 | 如何用YOLOv13训练自己的数据集(以明火烟雾检测举例)