首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >万能工具大全——体重记录追踪系统

万能工具大全——体重记录追踪系统

作者头像
红目香薰
发布2025-12-16 15:18:25
发布2025-12-16 15:18:25
2290
举报
文章被收录于专栏:CSDNToQQCodeCSDNToQQCode
在这里插入图片描述
在这里插入图片描述

前言

我们可以通过算法来完成很多很多的功能,所以就有了一个想法,将各类工具都写出来,当然是尽可能的,毕竟未来无限可期,很多功能是我们当前还想不到的,为了最为靠谱的方法来完成,这里选择的语言为 HTML 来完成,别看只是简单的页面操作,但是里面都是各类算法来完成的,并且有很多的js库,做起来会很方便,能节约很多的时间,本系列文章会很多,有些标题命名可能不太合适,如果你用到了,感觉不好找可以在文章下面留言,我看到了后会进行对应的修改,希望本系列文章能给大家提供到各种各样的遍历。

核心代码

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>体重记录追踪</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
    <style>
        :root {
            --primary-color: #5d9cec;
            --secondary-color: #f5f7fa;
            --accent-color: #4a89dc;
            --text-color: #333;
            --light-text: #666;
            --border-radius: 10px;
            --box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
        }
        
        body {
            font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
            background-color: #f8f9fa;
            color: var(--text-color);
            line-height: 1.6;
        }
        
        .header {
            background-color: white;
            box-shadow: var(--box-shadow);
            padding: 1.5rem 0;
            margin-bottom: 2rem;
        }
        
        .header h1 {
            color: var(--primary-color);
            font-weight: 600;
        }
        
        .container {
            max-width: 1200px;
        }
        
        .card {
            border: none;
            border-radius: var(--border-radius);
            box-shadow: var(--box-shadow);
            transition: transform 0.3s ease;
            margin-bottom: 1.5rem;
            overflow: hidden;
        }
        
        .card:hover {
            transform: translateY(-5px);
        }
        
        .card-header {
            background-color: var(--primary-color);
            color: white;
            font-weight: 600;
            padding: 0.8rem 1.2rem;
        }
        
        .btn-primary {
            background-color: var(--primary-color);
            border-color: var(--primary-color);
        }
        
        .btn-primary:hover {
            background-color: var(--accent-color);
            border-color: var(--accent-color);
        }
        
        .btn-outline-primary {
            color: var(--primary-color);
            border-color: var(--primary-color);
        }
        
        .btn-outline-primary:hover {
            background-color: var(--primary-color);
            color: white;
        }
        
        .weight-item {
            border-left: 4px solid var(--primary-color);
            background-color: white;
            padding: 1rem;
            margin-bottom: 0.5rem;
            border-radius: var(--border-radius);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
            transition: all 0.2s ease;
        }
        
        .weight-item:hover {
            box-shadow: var(--box-shadow);
        }
        
        .weight-item.gain {
            border-left-color: #dc3545;
        }
        
        .weight-item.loss {
            border-left-color: #28a745;
        }
        
        .weight-item.maintain {
            border-left-color: #ffc107;
        }
        
        .weight-value {
            font-weight: 600;
            font-size: 1.1rem;
        }
        
        .weight-change {
            font-size: 0.9rem;
            font-weight: 600;
        }
        
        .weight-change.gain {
            color: #dc3545;
        }
        
        .weight-change.loss {
            color: #28a745;
        }
        
        .weight-change.maintain {
            color: #ffc107;
        }
        
        .footer {
            background-color: white;
            padding: 2rem 0;
            margin-top: 3rem;
            box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.05);
        }
        
        .footer p {
            margin-bottom: 0;
            color: var(--light-text);
        }
        
        .summary-card {
            text-align: center;
            padding: 1.5rem;
        }
        
        .summary-value {
            font-size: 1.8rem;
            font-weight: bold;
            display: block;
            margin: 0.5rem 0;
        }
        
        .current-weight {
            color: var(--primary-color);
        }
        
        .target-weight {
            color: #28a745;
        }
        
        .initial-weight {
            color: #6c757d;
        }
        
        .chart-container {
            position: relative;
            height: 300px;
            margin-bottom: 1.5rem;
        }
        
        .date-filter {
            display: flex;
            justify-content: center;
            margin-bottom: 1.5rem;
        }
        
        .date-filter .btn {
            margin: 0 0.25rem;
        }
        
        .modal-content {
            border-radius: var(--border-radius);
        }
        
        .modal-header {
            background-color: var(--primary-color);
            color: white;
            border-top-left-radius: var(--border-radius);
            border-top-right-radius: var(--border-radius);
        }
        
        .modal-title {
            font-weight: 600;
        }
        
        .btn-close {
            filter: brightness(0) invert(1);
        }
        
        .progress {
            height: 10px;
            margin-bottom: 0.5rem;
        }
        
        .bmi-indicator {
            position: relative;
            height: 30px;
            background: linear-gradient(to right, #28a745, #ffc107, #dc3545);
            border-radius: 15px;
            margin: 1rem 0;
        }
        
        .bmi-marker {
            position: absolute;
            top: -10px;
            width: 10px;
            height: 30px;
            background-color: #333;
            transform: translateX(-50%);
        }
        
        .bmi-labels {
            display: flex;
            justify-content: space-between;
            margin-top: 5px;
            font-size: 0.8rem;
            color: var(--light-text);
        }
        
        .stats-item {
            text-align: center;
            padding: 1rem;
        }
        
        .stats-value {
            font-size: 1.5rem;
            font-weight: bold;
            color: var(--primary-color);
            display: block;
        }
        
        .stats-label {
            color: var(--light-text);
            font-size: 0.9rem;
        }
        
        @media (max-width: 768px) {
            .header {
                padding: 1rem 0;
                margin-bottom: 1rem;
            }
            
            .card {
                margin-bottom: 1rem;
            }
            
            .summary-value {
                font-size: 1.5rem;
            }
            
            .chart-container {
                height: 250px;
            }
            
            .weight-item {
                padding: 0.75rem;
            }
            
            .date-filter {
                flex-wrap: wrap;
            }
            
            .date-filter .btn {
                margin-bottom: 0.5rem;
            }
            
            .stats-value {
                font-size: 1.3rem;
            }
        }
    </style>
</head>
<body>
    <header class="header">
        <div class="container">
            <div class="row align-items-center">
                <div class="col-md-6">
                    <h1><i class="bi bi-clipboard2-pulse me-2"></i>体重记录追踪</h1>
                    <p class="text-muted">科学记录体重变化,健康管理更轻松</p>
                </div>
                <div class="col-md-6 text-md-end mt-3 mt-md-0">
                    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addWeightModal">
                        <i class="bi bi-plus-circle me-1"></i>记录今日体重
                    </button>
                </div>
            </div>
        </div>
    </header>

    <div class="container">
        <div class="row">
            <div class="col-md-4">
                <div class="card summary-card">
                    <div class="card-body">
                        <h5>当前体重</h5>
                        <span class="summary-value current-weight" id="currentWeight">0.0 kg</span>
                    </div>
                </div>
            </div>
            <div class="col-md-4">
                <div class="card summary-card">
                    <div class="card-body">
                        <h5>目标体重</h5>
                        <span class="summary-value target-weight" id="targetWeight">0.0 kg</span>
                    </div>
                </div>
            </div>
            <div class="col-md-4">
                <div class="card summary-card">
                    <div class="card-body">
                        <h5>初始体重</h5>
                        <span class="summary-value initial-weight" id="initialWeight">0.0 kg</span>
                    </div>
                </div>
            </div>
        </div>

        <div class="row mt-4">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header d-flex justify-content-between align-items-center">
                        <span>体重趋势</span>
                        <div class="dropdown">
                            <button class="btn btn-sm btn-light dropdown-toggle" type="button" id="chartTypeDropdown" data-bs-toggle="dropdown" aria-expanded="false">
                                图表类型
                            </button>
                            <ul class="dropdown-menu" aria-labelledby="chartTypeDropdown">
                                <li><a class="dropdown-item chart-type-item" href="#" data-type="line">折线图</a></li>
                                <li><a class="dropdown-item chart-type-item" href="#" data-type="bar">柱状图</a></li>
                            </ul>
                        </div>
                    </div>
                    <div class="card-body">
                        <div class="date-filter">
                            <button class="btn btn-sm btn-outline-primary active" data-period="month">本月</button>
                            <button class="btn btn-sm btn-outline-primary" data-period="3months">近三个月</button>
                            <button class="btn btn-sm btn-outline-primary" data-period="6months">近半年</button>
                            <button class="btn btn-sm btn-outline-primary" data-period="year">近一年</button>
                        </div>
                        
                        <div class="chart-container">
                            <canvas id="weightChart"></canvas>
                        </div>
                    </div>
                </div>
                
                <div class="card mt-4">
                    <div class="card-header">
                        <span>目标进度</span>
                    </div>
                    <div class="card-body">
                        <div class="progress" id="weightProgress">
                            <div class="progress-bar bg-success" role="progressbar" style="width: 0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
                        </div>
                        <div class="d-flex justify-content-between">
                            <small id="progressStart">0.0 kg</small>
                            <small id="progressCurrent">当前: 0.0 kg</small>
                            <small id="progressTarget">目标: 0.0 kg</small>
                        </div>
                        
                        <div class="mt-4">
                            <h6>BMI 指数: <span id="bmiValue">0.0</span> <small class="text-muted">(<span id="bmiCategory">未知</span>)</small></h6>
                            <div class="bmi-indicator">
                                <div class="bmi-marker" id="bmiMarker" style="left: 0%"></div>
                            </div>
                            <div class="bmi-labels">
                                <span>偏瘦</span>
                                <span>正常</span>
                                <span>超重</span>
                                <span>肥胖</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            
            <div class="col-md-4">
                <div class="card mb-4">
                    <div class="card-header">
                        <span>统计数据</span>
                    </div>
                    <div class="card-body">
                        <div class="row">
                            <div class="col-6">
                                <div class="stats-item">
                                    <span class="stats-value" id="totalLoss">0.0</span>
                                    <span class="stats-label">总减重 (kg)</span>
                                </div>
                            </div>
                            <div class="col-6">
                                <div class="stats-item">
                                    <span class="stats-value" id="avgWeeklyChange">0.0</span>
                                    <span class="stats-label">周平均变化 (kg)</span>
                                </div>
                            </div>
                            <div class="col-6">
                                <div class="stats-item">
                                    <span class="stats-value" id="recordDays">0</span>
                                    <span class="stats-label">记录天数</span>
                                </div>
                            </div>
                            <div class="col-6">
                                <div class="stats-item">
                                    <span class="stats-value" id="daysToGoal">0</span>
                                    <span class="stats-label">预计达标天数</span>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                
                <div class="card">
                    <div class="card-header">
                        <span>最近记录</span>
                    </div>
                    <div class="card-body p-2">
                        <div id="weightRecords">
                            <!-- 体重记录将通过JavaScript动态添加 -->
                        </div>
                        
                        <div id="emptyState" class="text-center py-4">
                            <i class="bi bi-clipboard-plus" style="font-size: 2rem; color: #ccc;"></i>
                            <p class="mt-2 text-muted">暂无体重记录</p>
                            <button class="btn btn-primary btn-sm mt-2" data-bs-toggle="modal" data-bs-target="#addWeightModal">
                                添加第一条记录
                            </button>
                        </div>
                    </div>
                </div>
                
                <div class="card mt-4">
                    <div class="card-header">
                        <span>设置目标</span>
                    </div>
                    <div class="card-body">
                        <form id="targetForm">
                            <div class="mb-3">
                                <label for="targetWeightInput" class="form-label">目标体重 (kg)</label>
                                <input type="number" class="form-control" id="targetWeightInput" step="0.1" min="30" max="200">
                            </div>
                            <div class="mb-3">
                                <label for="targetDateInput" class="form-label">目标日期</label>
                                <input type="date" class="form-control" id="targetDateInput">
                            </div>
                            <div class="d-grid">
                                <button type="button" class="btn btn-primary" id="saveTargetBtn">保存目标</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <!-- 添加体重记录模态框 -->
    <div class="modal fade" id="addWeightModal" tabindex="-1" aria-labelledby="addWeightModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="addWeightModalLabel">记录体重</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <form id="weightForm">
                        <div class="mb-3">
                            <label for="weightInput" class="form-label">体重 (kg)</label>
                            <input type="number" class="form-control" id="weightInput" step="0.1" min="30" max="200" required>
                        </div>
                        
                        <div class="mb-3">
                            <label for="dateInput" class="form-label">日期</label>
                            <input type="date" class="form-control" id="dateInput" required>
                        </div>
                        
                        <div class="mb-3">
                            <label for="noteInput" class="form-label">备注</label>
                            <textarea class="form-control" id="noteInput" rows="2"></textarea>
                        </div>
                        
                        <div class="mb-3">
                            <label class="form-label">身体数据 (可选)</label>
                            <div class="row g-2">
                                <div class="col-6">
                                    <div class="input-group">
                                        <span class="input-group-text">身高</span>
                                        <input type="number" class="form-control" id="heightInput" placeholder="cm" step="0.1" min="100" max="250">
                                    </div>
                                </div>
                                <div class="col-6">
                                    <div class="input-group">
                                        <span class="input-group-text">体脂率</span>
                                        <input type="number" class="form-control" id="bodyFatInput" placeholder="%" step="0.1" min="3" max="50">
                                    </div>
                                </div>
                            </div>
                        </div>
                    </form>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                    <button type="button" class="btn btn-primary" id="saveWeightBtn">保存</button>
                </div>
            </div>
        </div>
    </div>

    <footer class="footer">
        <div class="container">
            <div class="row">
                <div class="col-md-6">
                    <p>© 2025年8月 体重记录追踪系统</p>
                </div>
                <div class="col-md-6 text-md-end">
                    <p>科学管理体重,健康生活每一天</p>
                </div>
            </div>
        </div>
    </footer>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 初始化日期为今天
            document.getElementById('dateInput').valueAsDate = new Date();
            
            // 设置目标日期默认为一个月后
            const defaultTargetDate = new Date();
            defaultTargetDate.setMonth(defaultTargetDate.getMonth() + 1);
            document.getElementById('targetDateInput').valueAsDate = defaultTargetDate;
            
            // 体重数据存储
            let weightRecords = JSON.parse(localStorage.getItem('weightRecords')) || [];
            let userSettings = JSON.parse(localStorage.getItem('userSettings')) || {
                targetWeight: null,
                targetDate: null,
                height: null
            };
            
            // 更新UI
            updateUI();
            
            // 保存体重记录
            document.getElementById('saveWeightBtn').addEventListener('click', function() {
                const form = document.getElementById('weightForm');
                const weight = parseFloat(document.getElementById('weightInput').value);
                const date = document.getElementById('dateInput').value;
                const note = document.getElementById('noteInput').value;
                const height = parseFloat(document.getElementById('heightInput').value) || userSettings.height;
                const bodyFat = parseFloat(document.getElementById('bodyFatInput').value) || null;
                
                if (!weight || !date) {
                    alert('请填写必填字段');
                    return;
                }
                
                // 更新用户身高
                if (height) {
                    userSettings.height = height;
                    localStorage.setItem('userSettings', JSON.stringify(userSettings));
                }
                
                // 检查是否已存在该日期的记录
                const existingIndex = weightRecords.findIndex(r => r.date === date);
                
                if (existingIndex !== -1) {
                    // 更新现有记录
                    weightRecords[existingIndex] = {
                        id: weightRecords[existingIndex].id,
                        weight,
                        date,
                        note,
                        bodyFat,
                        timestamp: new Date().toISOString()
                    };
                } else {
                    // 添加新记录
                    const record = {
                        id: Date.now(),
                        weight,
                        date,
                        note,
                        bodyFat,
                        timestamp: new Date().toISOString()
                    };
                    weightRecords.push(record);
                }
                
                // 保存数据
                localStorage.setItem('weightRecords', JSON.stringify(weightRecords));
                
                // 重置表单
                form.reset();
                document.getElementById('dateInput').valueAsDate = new Date();
                
                // 关闭模态框
                const modal = bootstrap.Modal.getInstance(document.getElementById('addWeightModal'));
                modal.hide();
                
                // 更新UI
                updateUI();
            });
            
            // 保存目标
            document.getElementById('saveTargetBtn').addEventListener('click', function() {
                const targetWeight = parseFloat(document.getElementById('targetWeightInput').value);
                const targetDate = document.getElementById('targetDateInput').value;
                
                if (!targetWeight || !targetDate) {
                    alert('请填写目标体重和日期');
                    return;
                }
                
                userSettings.targetWeight = targetWeight;
                userSettings.targetDate = targetDate;
                localStorage.setItem('userSettings', JSON.stringify(userSettings));
                
                updateUI();
                alert('目标设置已保存');
            });
            
            // 图表类型切换
            document.querySelectorAll('.chart-type-item').forEach(item => {
                item.addEventListener('click', function(e) {
                    e.preventDefault();
                    const chartType = this.getAttribute('data-type');
                    document.getElementById('chartTypeDropdown').textContent = chartType === 'line' ? '折线图' : '柱状图';
                    updateWeightChart(weightRecords, chartType);
                });
            });
            
            // 日期筛选
            document.querySelectorAll('.date-filter .btn').forEach(btn => {
                btn.addEventListener('click', function() {
                    document.querySelectorAll('.date-filter .btn').forEach(b => b.classList.remove('active'));
                    this.classList.add('active');
                    const period = this.getAttribute('data-period');
                    filterByDate(period);
                });
            });
            
            // 删除体重记录
            document.addEventListener('click', function(e) {
                if (e.target && e.target.classList.contains('delete-btn')) {
                    const id = parseInt(e.target.getAttribute('data-id'));
                    if (confirm('确定要删除这条记录吗?')) {
                        weightRecords = weightRecords.filter(r => r.id !== id);
                        localStorage.setItem('weightRecords', JSON.stringify(weightRecords));
                        updateUI();
                    }
                }
            });
            
            // 更新UI函数
            function updateUI() {
                updateWeightRecords(weightRecords);
                updateSummary(weightRecords);
                updateWeightChart(weightRecords, 'line');
                updateProgress(weightRecords);
                updateStats(weightRecords);
                toggleEmptyState();
                
                // 填充目标设置表单
                if (userSettings.targetWeight) {
                    document.getElementById('targetWeightInput').value = userSettings.targetWeight;
                }
                if (userSettings.targetDate) {
                    document.getElementById('targetDateInput').value = userSettings.targetDate;
                }
            }
            
            // 更新体重记录列表
            function updateWeightRecords(data) {
                const recordsContainer = document.getElementById('weightRecords');
                recordsContainer.innerHTML = '';
                
                // 按日期降序排序
                data.sort((a, b) => new Date(b.date) - new Date(a.date));
                
                // 只显示最近10条记录
                const recentRecords = data.slice(0, 10);
                
                recentRecords.forEach((record, index) => {
                    const item = document.createElement('div');
                    
                    // 计算与上一条记录的体重差异
                    let changeClass = 'maintain';
                    let changeText = '维持';
                    let changeSymbol = '';
                    
                    if (index < recentRecords.length - 1) {
                        const prevWeight = recentRecords[index + 1].weight;
                        const weightDiff = record.weight - prevWeight;
                        
                        if (weightDiff > 0) {
                            changeClass = 'gain';
                            changeText = `增加 ${weightDiff.toFixed(1)}kg`;
                            changeSymbol = '↑';
                        } else if (weightDiff < 0) {
                            changeClass = 'loss';
                            changeText = `减少 ${Math.abs(weightDiff).toFixed(1)}kg`;
                            changeSymbol = '↓';
                        }
                    }
                    
                    item.className = `weight-item ${changeClass}`;
                    
                    const formattedDate = formatDate(record.date);
                    
                    item.innerHTML = `
                        <div class="d-flex justify-content-between align-items-center">
                            <div>
                                <div class="weight-value">${record.weight.toFixed(1)} kg</div>
                                <div class="text-muted small">${formattedDate} ${record.note ? '· ' + record.note : ''}</div>
                            </div>
                            <div class="d-flex align-items-center">
                                <span class="weight-change ${changeClass} me-3">${changeSymbol} ${changeText}</span>
                                <button class="btn btn-sm btn-outline-danger delete-btn" data-id="${record.id}">
                                    <i class="bi bi-trash"></i>
                                </button>
                            </div>
                        </div>
                    `;
                    
                    recordsContainer.appendChild(item);
                });
            }
            
            // 更新汇总信息
            function updateSummary(data) {
                // 按日期排序
                data.sort((a, b) => new Date(a.date) - new Date(b.date));
                
                const currentWeight = data.length > 0 ? data[data.length - 1].weight : 0;
                const initialWeight = data.length > 0 ? data[0].weight : 0;
                const targetWeight = userSettings.targetWeight || 0;
                
                document.getElementById('currentWeight').textContent = `${currentWeight.toFixed(1)} kg`;
                document.getElementById('initialWeight').textContent = `${initialWeight.toFixed(1)} kg`;
                document.getElementById('targetWeight').textContent = `${targetWeight.toFixed(1)} kg`;
                
                // 计算BMI
                if (currentWeight > 0 && userSettings.height) {
                    const heightInMeters = userSettings.height / 100;
                    const bmi = currentWeight / (heightInMeters * heightInMeters);
                    
                    document.getElementById('bmiValue').textContent = bmi.toFixed(1);
                    
                    // 设置BMI分类
                    let category = '';
                    let markerPosition = 0;
                    
                    if (bmi < 18.5) {
                        category = '偏瘦';
                        markerPosition = (bmi / 18.5) * 25;
                    } else if (bmi < 24) {
                        category = '正常';
                        markerPosition = 25 + ((bmi - 18.5) / 5.5) * 25;
                    } else if (bmi < 28) {
                        category = '超重';
                        markerPosition = 50 + ((bmi - 24) / 4) * 25;
                    } else {
                        category = '肥胖';
                        markerPosition = 75 + Math.min(((bmi - 28) / 12) * 25, 25);
                    }
                    
                    document.getElementById('bmiCategory').textContent = category;
                    document.getElementById('bmiMarker').style.left = `${markerPosition}%`;
                } else {
                    document.getElementById('bmiValue').textContent = '0.0';
                    document.getElementById('bmiCategory').textContent = '未知';
                    document.getElementById('bmiMarker').style.left = '0%';
                }
            }
            
            // 更新体重图表
            function updateWeightChart(data, chartType = 'line') {
                const ctx = document.getElementById('weightChart').getContext('2d');
                
                // 按日期排序
                data.sort((a, b) => new Date(a.date) - new Date(b.date));
                
                // 准备图表数据
                const labels = data.map(r => formatDate(r.date));
                const weights = data.map(r => r.weight);
                
                // 销毁旧图表
                if (window.weightChart) {
                    window.weightChart.destroy();
                }
                
                // 创建新图表
                window.weightChart = new Chart(ctx, {
                    type: chartType,
                    data: {
                        labels: labels,
                        datasets: [{
                            label: '体重 (kg)',
                            data: weights,
                            borderColor: '#5d9cec',
                            backgroundColor: 'rgba(93, 156, 236, 0.1)',
                            borderWidth: 2,
                            fill: chartType === 'line',
                            tension: 0.4
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: {
                            legend: {
                                display: false
                            }
                        },
                        scales: {
                            y: {
                                beginAtZero: false,
                                ticks: {
                                    callback: function(value) {
                                        return value + ' kg';
                                    }
                                }
                            }
                        }
                    }
                });
                
                // 如果没有数据,显示无数据提示
                if (data.length === 0) {
                    ctx.font = '14px Arial';
                    ctx.textAlign = 'center';
                    ctx.fillText('暂无数据', ctx.canvas.width / 2, ctx.canvas.height / 2);
                }
            }
            
            // 更新进度条
            function updateProgress(data) {
                if (data.length === 0 || !userSettings.targetWeight) {
                    return;
                }
                
                // 按日期排序
                data.sort((a, b) => new Date(a.date) - new Date(b.date));
                
                const initialWeight = data[0].weight;
                const currentWeight = data[data.length - 1].weight;
                const targetWeight = userSettings.targetWeight;
                
                // 计算进度
                let progress = 0;
                
                // 如果目标是减重
                if (initialWeight > targetWeight) {
                    progress = Math.min(100, Math.max(0, 
                        ((initialWeight - currentWeight) / (initialWeight - targetWeight)) * 100
                    ));
                } 
                // 如果目标是增重
                else if (initialWeight < targetWeight) {
                    progress = Math.min(100, Math.max(0, 
                        ((currentWeight - initialWeight) / (targetWeight - initialWeight)) * 100
                    ));
                }
                // 如果初始体重等于目标体重
                else {
                    progress = currentWeight === targetWeight ? 100 : 0;
                }
                
                // 更新进度条
                const progressBar = document.querySelector('#weightProgress .progress-bar');
                progressBar.style.width = `${progress}%`;
                progressBar.setAttribute('aria-valuenow', progress);
                
                // 更新进度文本
                document.getElementById('progressStart').textContent = `${initialWeight.toFixed(1)} kg`;
                document.getElementById('progressCurrent').textContent = `当前: ${currentWeight.toFixed(1)} kg`;
                document.getElementById('progressTarget').textContent = `目标: ${targetWeight.toFixed(1)} kg`;
            }
            
            // 更新统计数据
            function updateStats(data) {
                if (data.length === 0) {
                    return;
                }
                
                // 按日期排序
                data.sort((a, b) => new Date(a.date) - new Date(b.date));
                
                const initialWeight = data[0].weight;
                const currentWeight = data[data.length - 1].weight;
                const totalLoss = initialWeight - currentWeight;
                
                // 计算周平均变化
                let weeklyChange = 0;
                if (data.length > 1) {
                    const firstDate = new Date(data[0].date);
                    const lastDate = new Date(data[data.length - 1].date);
                    const daysDiff = (lastDate - firstDate) / (1000 * 60 * 60 * 24);
                    const weeksDiff = daysDiff / 7;
                    
                    if (weeksDiff > 0) {
                        weeklyChange = totalLoss / weeksDiff;
                    }
                }
                
                // 计算预计达标天数
                let daysToGoal = 0;
                if (userSettings.targetWeight && weeklyChange !== 0) {
                    const remainingLoss = currentWeight - userSettings.targetWeight;
                    const dailyChange = weeklyChange / 7;
                    
                    if (dailyChange !== 0) {
                        daysToGoal = Math.abs(Math.ceil(remainingLoss / dailyChange));
                    }
                }
                
                // 更新UI
                document.getElementById('totalLoss').textContent = Math.abs(totalLoss).toFixed(1);
                document.getElementById('avgWeeklyChange').textContent = Math.abs(weeklyChange).toFixed(1);
                document.getElementById('recordDays').textContent = data.length;
                document.getElementById('daysToGoal').textContent = daysToGoal > 0 ? daysToGoal : '-';
            }
            
            // 按日期筛选
            function filterByDate(period) {
                const now = new Date();
                let startDate;
                
                switch (period) {
                    case 'month':
                        startDate = new Date(now.getFullYear(), now.getMonth(), 1);
                        break;
                    case '3months':
                        startDate = new Date(now);
                        startDate.setMonth(now.getMonth() - 3);
                        break;
                    case '6months':
                        startDate = new Date(now);
                        startDate.setMonth(now.getMonth() - 6);
                        break;
                    case 'year':
                        startDate = new Date(now);
                        startDate.setFullYear(now.getFullYear() - 1);
                        break;
                    default:
                        startDate = new Date(0); // 从1970年开始
                }
                
                // 筛选数据
                const filteredData = weightRecords.filter(r => new Date(r.date) >= startDate);
                
                // 更新图表
                updateWeightChart(filteredData, window.weightChart ? window.weightChart.config.type : 'line');
            }
            
            // 切换空状态显示
            function toggleEmptyState() {
                const emptyState = document.getElementById('emptyState');
                const recordsContainer = document.getElementById('weightRecords');
                
                if (weightRecords.length === 0) {
                    emptyState.style.display = 'block';
                    recordsContainer.style.display = 'none';
                } else {
                    emptyState.style.display = 'none';
                    recordsContainer.style.display = 'block';
                }
            }
            
            // 格式化日期
            function formatDate(dateString) {
                const date = new Date(dateString);
                return date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' });
            }
            
            // 添加示例数据(仅首次使用)
            function addSampleData() {
                if (weightRecords.length === 0 && !localStorage.getItem('sampleDataAdded')) {
                    const today = new Date();
                    const sampleData = [];
                    
                    // 生成过去30天的示例数据
                    for (let i = 30; i >= 0; i -= 2) {
                        const date = new Date(today);
                        date.setDate(today.getDate() - i);
                        
                        // 模拟体重变化(从75kg开始,逐渐减少到72kg)
                        const weight = 75 - (i * 0.1);
                        
                        sampleData.push({
                            id: Date.now() - i * 100000,
                            weight: parseFloat(weight.toFixed(1)),
                            date: date.toISOString().split('T')[0],
                            note: i === 0 ? '今日记录' : '',
                            timestamp: date.toISOString()
                        });
                    }
                    
                    weightRecords = sampleData;
                    localStorage.setItem('weightRecords', JSON.stringify(weightRecords));
                    localStorage.setItem('sampleDataAdded', 'true');
                    
                    // 设置示例目标和身高
                    userSettings.targetWeight = 70;
                    userSettings.height = 175;
                    userSettings.targetDate = new Date(today.setMonth(today.getMonth() + 2)).toISOString().split('T')[0];
                    localStorage.setItem('userSettings', JSON.stringify(userSettings));
                    
                    updateUI();
                }
            }
            
            // 添加示例数据
            addSampleData();
        });
    </script>

效果截图

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

体重记录追踪系统功能说明

系统概述

体重记录追踪系统是一款专为用户提供体重管理和健康监测的Web应用。通过直观的界面和丰富的数据可视化功能,帮助用户科学地记录、分析和管理自己的体重变化,从而实现健康的体重管理目标。

核心功能

1. 体重记录管理
  • 添加体重记录:用户可以输入体重数据、日期、备注等信息
  • 查看历史记录:以列表形式展示最近的体重记录,包括日期、体重值和变化趋势
  • 编辑和删除:支持修改或删除已有的体重记录
  • 可选身体数据:除体重外,还可记录身高、体脂率等身体数据
2. 数据可视化
  • 体重趋势图表:通过折线图或柱状图直观展示体重变化趋势
  • 时间范围筛选:支持查看本月、近三个月、近半年、近一年的体重变化
  • 图表类型切换:可在折线图和柱状图之间切换,满足不同的数据查看需求
3. 目标设置与追踪
  • 设置目标体重:用户可设定目标体重和达成日期
  • 进度追踪:通过进度条直观展示当前体重与目标体重的差距
  • 预计达标天数:根据当前减重/增重速度,预估达到目标所需天数
4. 健康指标分析
  • BMI指数计算:根据体重和身高自动计算BMI指数
  • BMI分类显示:直观展示BMI所属类别(偏瘦、正常、超重、肥胖)
  • 体重变化统计:显示总减重量、周平均变化等关键指标
5. 数据统计与分析
  • 总减重/增重量:展示从初始记录到当前的总体重变化
  • 周平均变化:计算每周平均体重变化量
  • 记录天数统计:显示已记录的总天数
  • 趋势预测:基于历史数据预测未来体重变化趋势

技术特点

  • 本地数据存储:使用浏览器本地存储(localStorage)保存用户数据,保护隐私
  • 响应式设计:适配各种屏幕尺寸,提供良好的移动端体验
  • 无需注册:无需账号即可使用,简单便捷
  • 数据可视化:使用Chart.js提供专业的数据图表展示
  • Bootstrap界面:采用Bootstrap 5框架,界面美观现代

使用方法

  1. 记录体重:点击页面顶部的"记录今日体重"按钮,在弹出的窗口中填写体重、日期等信息
  2. 设置目标:在右侧"设置目标"卡片中,输入目标体重和目标日期,点击"保存目标"
  3. 查看趋势:在"体重趋势"图表中查看体重变化,可通过上方按钮切换不同时间范围
  4. 查看统计:在"统计数据"卡片中查看总减重量、周平均变化等关键指标
  5. 管理记录:在"最近记录"列表中查看、删除历史记录

数据隐私

  • 所有数据仅存储在用户本地浏览器中,不会上传至任何服务器
  • 清除浏览器缓存会导致数据丢失,建议定期导出备份重要数据

系统要求

  • 支持现代浏览器:Chrome、Firefox、Safari、Edge等
  • 需启用JavaScript和localStorage
  • 建议使用最新版本浏览器以获得最佳体验
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 核心代码
  • 效果截图
  • 体重记录追踪系统功能说明
    • 系统概述
    • 核心功能
      • 1. 体重记录管理
      • 2. 数据可视化
      • 3. 目标设置与追踪
      • 4. 健康指标分析
      • 5. 数据统计与分析
    • 技术特点
    • 使用方法
    • 数据隐私
    • 系统要求
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档