欢迎加入开源鸿蒙PC社区:
https://harmonypc.csdn.net/

atomgit仓库地址: https://atomgit.com/ai_lingshi/jianshenqicai

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

一、项目概述

1.1 项目背景

健身房的器材管理是健身行业运营的核心环节。传统的器材管理方式依赖纸质记录或简单的电子表格,存在以下问题:

问题影响
信息不集中需要在多个地方查找器材信息
维护提醒不及时容易错过器材维护时间
使用统计困难难以分析器材使用频率
状态更新滞后无法实时了解器材可用性

本项目旨在通过数字化手段,实现健身房器材的智能化管理,提升运营效率。

1.2 功能定位

核心功能

  • 器材信息集中管理
  • 实时状态追踪
  • 智能维护提醒
  • 使用数据分析

用户群体

  • 健身房管理员
  • 器材维护人员
  • 健身教练
  • 场馆运营者

1.3 技术架构

┌─────────────────────────────────────────────────────────────┐
│                     视图层 (View)                           │
│    ┌─────────────┐  ┌─────────────┐  ┌─────────────┐     │
│    │   HTML     │  │    CSS      │  │  组件卡片    │     │
│    │  (结构)    │  │  (样式)    │  │  (渲染)    │     │
│    └─────────────┘  └─────────────┘  └─────────────┘     │
├─────────────────────────────────────────────────────────────┤
│                     控制层 (Controller)                     │
│    ┌─────────────────────────────────────────────────┐    │
│    │           EquipmentController                   │    │
│    │  - 事件监听与处理                               │    │
│    │  - 业务逻辑编排                                  │    │
│    │  - 视图更新协调                                  │    │
│    └─────────────────────────────────────────────────┘    │
├─────────────────────────────────────────────────────────────┤
│                     模型层 (Model)                          │
│    ┌─────────────────────────────────────────────────┐    │
│    │           EquipmentModel                        │    │
│    │  - 数据结构定义                                  │    │
│    │  - CRUD 操作实现                                │    │
│    │  - 业务规则校验                                  │    │
│    │  - 统计分析计算                                  │    │
│    └─────────────────────────────────────────────────┘    │
├─────────────────────────────────────────────────────────────┤
│                     存储层 (Storage)                        │
│              localStorage 本地持久化                        │
└─────────────────────────────────────────────────────────────┘

二、数据模型设计

2.1 器材数据结构

{
    id: Number,                    // 唯一标识符
    name: String,                  // 器材名称
    category: String,              // 器材分类
    status: String,                // 当前状态
    purchaseDate: String,         // 购买日期
    lastMaintenance: String,       // 上次维护日期
    nextMaintenance: String,       // 下次维护日期
    usageCount: Number,            // 使用次数
    notes: String                  // 备注信息
}

2.2 分类枚举

const CATEGORIES = {
    cardio: 'cardio',      // 有氧器材
    strength: 'strength',  // 力量器材
    freeweight: 'freeweight',  // 自由重量
    stretch: 'stretch',    // 拉伸器材
    other: 'other'         // 其他器材
};

器材分类详解

分类代码典型器材特点
有氧器材cardio跑步机、椭圆机、划船机、动感单车高频使用,需定期保养
力量器材strength卧推架、深蹲架、龙门架、腿举机承重要求高,安全第一
自由重量freeweight哑铃组、杠铃组、壶铃使用灵活,损耗较快
拉伸器材stretch瑜伽垫、泡沫轴、拉伸带低损耗,使用频率高
其他器材other跳绳、健身球、战绳辅助训练,易于管理

2.3 状态枚举

const STATUS = {
    available: 'available',    // 可用
    inuse: 'inuse',            // 使用中
    maintenance: 'maintenance'  // 维护中
};

状态流转规则

可用 ──────────→ 使用中
   ↑                  │
   │                  ↓
   └──────────←─── 使用完成
                   
可用 ──────────→ 维护中
   ↑                  │
   │                  ↓
   └──────────←─── 维护完成

三、核心功能实现

3.1 数据模型类实现

class EquipmentModel {
    constructor() {
        this.equipment = [];
        this.loadData();
    }

    // 加载数据
    loadData() {
        const saved = localStorage.getItem('fitnessEquipment');
        if (saved) {
            try {
                this.equipment = JSON.parse(saved);
            } catch (e) {
                this.equipment = this.getDefaultData();
            }
        } else {
            this.equipment = this.getDefaultData();
            this.saveData();
        }
    }

    // 保存数据
    saveData() {
        localStorage.setItem('fitnessEquipment', 
            JSON.stringify(this.equipment));
    }
}

3.2 CRUD 操作实现

// 添加器材
add(equipment) {
    const newId = Math.max(0, ...this.equipment.map(e => e.id)) + 1;
    const newEquipment = {
        id: newId,
        ...equipment,
        usageCount: equipment.usageCount || 0
    };
    this.equipment.push(newEquipment);
    this.saveData();
    return newEquipment;
}

// 更新器材
update(id, data) {
    const index = this.equipment.findIndex(e => e.id === id);
    if (index !== -1) {
        this.equipment[index] = { 
            ...this.equipment[index], 
            ...data 
        };
        this.saveData();
        return this.equipment[index];
    }
    return null;
}

// 删除器材
delete(id) {
    const index = this.equipment.findIndex(e => e.id === id);
    if (index !== -1) {
        const deleted = this.equipment.splice(index, 1)[0];
        this.saveData();
        return deleted;
    }
    return null;
}

3.3 搜索与筛选

// 搜索器材
search(keyword) {
    const lowerKeyword = keyword.toLowerCase();
    return this.equipment.filter(e => 
        e.name.toLowerCase().includes(lowerKeyword) ||
        e.category.toLowerCase().includes(lowerKeyword) ||
        (e.notes && e.notes.toLowerCase().includes(lowerKeyword))
    );
}

// 按分类筛选
filterByCategory(category) {
    if (category === 'all') return this.getAll();
    return this.equipment.filter(e => e.category === category);
}

// 按状态筛选
filterByStatus(status) {
    if (status === 'all') return this.getAll();
    return this.equipment.filter(e => e.status === status);
}

搜索优化策略

策略说明适用场景
模糊匹配支持部分关键词搜索用户输入不完整时
大小写不敏感忽略大小写差异提高搜索灵活性
多字段搜索同时搜索名称、分类、备注增加搜索覆盖面

四、统计分析功能

4.1 统计数据计算

// 获取统计数据
getStats() {
    const total = this.equipment.length;
    const available = this.equipment
        .filter(e => e.status === 'available').length;
    const inuse = this.equipment
        .filter(e => e.status === 'inuse').length;
    const maintenance = this.equipment
        .filter(e => e.status === 'maintenance').length;
    
    return { total, available, inuse, maintenance };
}

4.2 使用频率排行

// 获取使用频率TOP5
getTopUsage() {
    return [...this.equipment]
        .sort((a, b) => b.usageCount - a.usageCount)
        .slice(0, 5);
}

排序算法分析

// 时间复杂度: O(n log n)
// 空间复杂度: O(n)

const sorted = [...this.equipment]
    .sort((a, b) => b.usageCount - a.usageCount)
    .slice(0, 5);

// 或者使用更高效的方式(只需找到前5个)
const sorted = this.equipment
    .sort((a, b) => b.usageCount - a.usageCount)
    .slice(0, 5);

4.3 维护提醒系统

// 获取维护提醒
getMaintenanceAlerts() {
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    
    return this.equipment
        .filter(e => e.nextMaintenance)
        .map(e => {
            const nextDate = new Date(e.nextMaintenance);
            nextDate.setHours(0, 0, 0, 0);
            const daysUntil = Math.ceil(
                (nextDate - today) / (1000 * 60 * 60 * 24)
            );
            
            return {
                ...e,
                daysUntil,
                isOverdue: daysUntil < 0,
                isUrgent: daysUntil >= 0 && daysUntil <= 7
            };
        })
        .filter(e => e.isOverdue || e.isUrgent)
        .sort((a, b) => a.daysUntil - b.daysUntil);
}

维护提醒等级

等级条件提示颜色优先级
逾期daysUntil < 0红色P0
紧急0 <= daysUntil <= 3橙色P1
预警4 <= daysUntil <= 7黄色P2

五、控制器设计

5.1 控制器职责

class EquipmentController {
    constructor() {
        this.model = new EquipmentModel();
        this.initDOMReferences();
        this.bindEventListeners();
        this.render();
    }
}

控制器核心职责

职责方法说明
DOM 引用初始化initDOMReferences()缓存 DOM 元素引用
事件绑定bindEventListeners()注册事件监听器
渲染协调render()协调各模块渲染
表单处理handleSave()处理新增/编辑表单提交
搜索处理handleSearch()处理搜索请求
使用记录handleRecordUsage()处理使用记录

5.2 事件处理机制

bindEventListeners() {
    // 搜索
    this.searchBtn.addEventListener('click', () => this.handleSearch());
    this.searchInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') this.handleSearch();
    });

    // 筛选
    this.categoryFilter.addEventListener('change', () => this.render());
    this.statusFilter.addEventListener('change', () => this.render());

    // 添加按钮
    this.addBtn.addEventListener('click', () => this.openAddModal());

    // 器材表单
    this.equipmentForm.addEventListener('submit', (e) => {
        e.preventDefault();
        this.handleSave();
    });
}

5.3 渲染策略

render() {
    // 获取筛选条件
    const category = this.categoryFilter.value;
    const status = this.statusFilter.value;
    
    // 获取数据
    let equipment = this.model.getAll();
    
    // 应用筛选
    if (category !== 'all') {
        equipment = equipment.filter(e => e.category === category);
    }
    if (status !== 'all') {
        equipment = equipment.filter(e => e.status === status);
    }
    
    // 渲染各模块
    this.renderEquipmentList(equipment);
    this.renderStats();
    this.renderTopUsage();
    this.renderMaintenanceAlerts();
}

渲染优化

优化点实现方式效果
DOM 缓存初始化时缓存所有 DOM 引用减少 DOM 查询
条件渲染器材列表为空时显示空状态提升用户体验
批量更新一次性生成 HTML 字符串减少重绘次数

六、卡片组件设计

6.1 器材卡片模板

renderEquipmentCard(item) {
    const categoryNames = {
        cardio: '有氧器材',
        strength: '力量器材',
        freeweight: '自由重量',
        stretch: '拉伸器材',
        other: '其他器材'
    };

    const statusNames = {
        available: '可用',
        inuse: '使用中',
        maintenance: '维护中'
    };

    // 计算维护提醒
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const nextDate = item.nextMaintenance ? 
        new Date(item.nextMaintenance) : null;
    if (nextDate) nextDate.setHours(0, 0, 0, 0);
    const daysUntilMaintenance = nextDate ? 
        Math.ceil((nextDate - today) / (1000 * 60 * 60 * 24)) : null;
    const showMaintenanceWarning = 
        daysUntilMaintenance !== null && daysUntilMaintenance <= 7;

    let warningHtml = '';
    if (showMaintenanceWarning) {
        if (daysUntilMaintenance < 0) {
            warningHtml = `
                <div class="maintenance-warning">
                    ⚠️ 维护已逾期 ${Math.abs(daysUntilMaintenance)} 天
                </div>`;
        } else if (daysUntilMaintenance <= 3) {
            warningHtml = `
                <div class="maintenance-warning">
                    ⚠️ ${daysUntilMaintenance} 天后需要维护
                </div>`;
        }
    }

    return `
        <div class="equipment-card" data-id="${item.id}">
            <div class="card-header">
                <div class="card-title">${item.name}</div>
                <span class="status-badge ${item.status}">
                    ${statusNames[item.status]}
                </span>
            </div>
            <span class="card-category">
                ${categoryNames[item.category]}
            </span>
            <div class="card-stats">
                <span>📊 ${item.usageCount} 次</span>
                ${item.purchaseDate ? 
                    `<span>📅 ${item.purchaseDate}</span>` : ''}
            </div>
            ${item.notes ? 
                `<div class="card-info"><div>${item.notes}</div></div>` : ''}
            ${warningHtml}
            <div class="card-actions">
                <button class="card-btn primary" 
                    onclick="equipmentController.openRecordUsageModal(${item.id})">
                    📝 使用
                </button>
                <button class="card-btn secondary" 
                    onclick="equipmentController.openEditModal(${item.id})">
                    ✏️ 编辑
                </button>
                <button class="card-btn danger" 
                    onclick="equipmentController.deleteEquipment(${item.id})">
                    🗑️ 删除
                </button>
            </div>
        </div>
    `;
}

6.2 卡片布局设计

.equipment-card {
    background: rgba(255, 255, 255, 0.95);
    border-radius: 16px;
    padding: 20px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
    transition: all 0.3s ease;
}

.equipment-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
}

卡片信息层级

层级内容视觉权重
1器材名称、状态标签高(标题级)
2器材分类、统计数据中(信息级)
3备注、维护警告低(辅助级)
4操作按钮功能入口

七、弹窗组件设计

7.1 添加/编辑弹窗

openAddModal() {
    this.modalTitle.textContent = '添加器材';
    document.getElementById('equipmentId').value = '';
    this.equipmentForm.reset();
    this.equipmentModal.classList.add('active');
}

openEditModal(id) {
    const equipment = this.model.getById(id);
    if (!equipment) return;

    this.modalTitle.textContent = '编辑器材';
    document.getElementById('equipmentId').value = equipment.id;
    document.getElementById('name').value = equipment.name;
    document.getElementById('category').value = equipment.category;
    document.getElementById('status').value = equipment.status;
    // ... 其他字段
    
    this.equipmentModal.classList.add('active');
}

7.2 使用记录弹窗

openRecordUsageModal(id) {
    const equipment = this.model.getById(id);
    if (!equipment) return;

    document.getElementById('usageEquipmentId').value = id;
    this.usageEquipmentName.textContent = `正在记录: ${equipment.name}`;
    document.getElementById('usageDuration').value = '30';
    document.getElementById('usageDate').value = 
        new Date().toISOString().split('T')[0];
    
    this.usageModal.classList.add('active');
}

7.3 弹窗样式

.modal {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(4px);
    z-index: 1000;
    align-items: center;
    justify-content: center;
}

.modal.active {
    display: flex;
}

.modal-content {
    background: #fff;
    border-radius: 16px;
    width: 100%;
    max-width: 500px;
    max-height: 90vh;
    overflow-y: auto;
    animation: modalSlideIn 0.3s ease;
}

@keyframes modalSlideIn {
    from {
        opacity: 0;
        transform: translateY(-20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

八、统计面板设计

8.1 统计数据展示

renderStats() {
    const stats = this.model.getStats();
    this.totalEquipment.textContent = stats.total;
    this.availableCount.textContent = stats.available;
    this.inuseCount.textContent = stats.inuse;
    this.maintenanceCount.textContent = stats.maintenance;
}

统计面板布局

.stats-list {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 15px;
}

.stat-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 15px;
    background: #f8f9fa;
    border-radius: 12px;
}

.stat-value {
    font-size: 1.8rem;
    font-weight: 700;
    font-family: 'Courier New', monospace;
}

.stat-value.available { color: #28a745; }
.stat-value.inuse { color: #ffc107; }
.stat-value.maintenance { color: #dc3545; }

8.2 TOP 5 排行榜

renderTopUsage() {
    const top = this.model.getTopUsage();
    
    if (top.length === 0) {
        this.topEquipment.innerHTML = 
            '<p style="color: #888; text-align: center;">暂无数据</p>';
        return;
    }

    this.topEquipment.innerHTML = top.map((item, index) => `
        <div class="top-item">
            <div class="top-rank">${index + 1}</div>
            <div class="top-name">${item.name}</div>
            <div class="top-count">${item.usageCount}次</div>
        </div>
    `).join('');
}

九、持久化存储

9.1 localStorage 策略

saveData() {
    localStorage.setItem('fitnessEquipment', 
        JSON.stringify(this.equipment));
}

loadData() {
    const saved = localStorage.getItem('fitnessEquipment');
    if (saved) {
        try {
            this.equipment = JSON.parse(saved);
        } catch (e) {
            // JSON 解析失败,使用默认数据
            this.equipment = this.getDefaultData();
        }
    } else {
        // 首次使用,初始化默认数据
        this.equipment = this.getDefaultData();
        this.saveData();
    }
}

9.2 数据安全处理

// 异常处理
try {
    this.equipment = JSON.parse(saved);
} catch (e) {
    console.error('数据解析失败:', e);
    this.equipment = this.getDefaultData();
}

// 数据备份建议
// 1. 定期导出 JSON 文件
// 2. 实现数据版本管理
// 3. 添加数据校验机制

十、扩展性设计

10.1 可配置化

// 分类配置
const CATEGORY_CONFIG = {
    cardio: { 
        name: '有氧器材', 
        icon: '🏃',
        maintenanceInterval: 30  // 天
    },
    strength: {
        name: '力量器材',
        icon: '🏋️',
        maintenanceInterval: 60
    },
    // ...
};

10.2 扩展功能建议

功能优先级实现难度说明
数据导出CSV/Excel 导出
二维码管理器材扫码识别
预约系统器材使用预约
使用分析图表统计分析
权限管理多用户权限控制
移动端适配响应式优化

十一、总结

11.1 架构设计亮点

设计点实现方式优势
MVC 模式Model/View/Controller 分离职责清晰,易于维护
数据驱动localStorage 持久化数据不丢失
事件委托Controller 统一处理减少事件绑定
组件化卡片、弹窗独立可复用性强

11.2 性能优化

优化项实现方式效果
DOM 缓存初始化时缓存引用减少查询次数
批量渲染一次性生成 HTML减少重绘
条件渲染空状态独立显示提升首屏速度

11.3 用户体验

特性实现方式价值
实时反馈Toast 提示操作确认
智能提醒维护预警系统预防性维护
视觉反馈状态颜色区分信息直观
快捷操作键盘支持提升效率

健身室器材管理系统通过现代化的前端架构设计,实现了器材管理的数字化、智能化,提升了健身房运营效率,降低了管理成本。

Logo

赋能鸿蒙PC开发者,共建全场景原生生态,共享一次开发多端部署创新价值。

更多推荐