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

基于Flask + ECharts的个人财务仪表盘 -(上个记账本的优化MAX)

      这次呢优化了很多东西,包括进账和出账,更重要的是可以以图表格的方式更加直观,还有仪表盘,在你加数据的时候可以实时变动(附上效果图)

 项目特色功能

  •  收支全面管理 - 支持收入和支出记录

  •  实时数据统计 - 自动计算总收入、总支出、结余

  •  预算管理 - 设置月度预算,实时监控预算使用情况

  •  多维度可视化 - 月度趋势图、消费分类饼图

  •  响应式设计 - 完美适配各种屏幕尺寸

  •  实时更新 - 无刷新数据交互

 技术架构

后端技术栈

  • Flask - 轻量级Web框架

  • JSON文件存储 - 简单可靠的数据持久化

  • RESTful API - 规范的接口设计

前端技术栈

  • ECharts - 百度开源可视化库

  • 原生JavaScript - 纯JS实现,无框架依赖

  • CSS3 Grid/Flex - 现代布局方案

  • 响应式设计 - 移动端友好

 项目结构

text

finance-dashboard/
├── app.py              # Flask主应用
├── data.json           # 数据存储文件
├── templates/
│   └── dashboard.html  # 主页面模板
├── static/
│   ├── style.css       # 样式文件
│   └── script.js       # 前端逻辑
└── ***.bat             #导出.bat格式参考上一篇

 核心代码解析

1. Flask后端应用 (app.py)

python

from flask import Flask, jsonify, render_template, request, send_from_directory
import json
import os
from datetime import datetimeapp = Flask(__name__)
DATA_FILE = 'data.json'def init_data_file():"""初始化数据文件"""if not os.path.exists(DATA_FILE):sample_data = {"transactions": [{"id": 1,"date": "2024-03-20","category": "餐饮","amount": -85,"type": "支出","note": "午餐"},{"id": 2,"date": "2024-03-19","category": "工资", "amount": 15000,"type": "收入","note": "月度工资"}],"budget": 10000}save_data(sample_data)@app.route('/api/dashboard-data')
def get_dashboard_data():"""获取仪表盘数据"""data = load_data()transactions = data.get('transactions', [])budget = data.get('budget', 10000)dashboard_data = {'overview': calculate_overview(transactions, budget),'monthly_trend': generate_monthly_trend(transactions),'category_data': generate_category_data(transactions),'recent_transactions': sorted(transactions, key=lambda x: x['date'], reverse=True)[:10]}return jsonify(dashboard_data)

2. 数据统计逻辑

python

def calculate_overview(transactions, budget):"""计算概览数据"""total_income = sum(t['amount'] for t in transactions if t['amount'] > 0)total_expense = abs(sum(t['amount'] for t in transactions if t['amount'] < 0))balance = total_income - total_expensereturn {'total_income': total_income,'total_expense': total_expense, 'balance': balance,'budget': budget}

3. 前端数据可视化 (script.js)

javascript

// 初始化ECharts图表
function initCharts() {trendChart = echarts.init(document.getElementById('trend-chart'));categoryChart = echarts.init(document.getElementById('category-chart'));
}// 渲染月度趋势图
function renderTrendChart(trendData) {const option = {title: { text: '月度收支趋势', left: 'center' },tooltip: { trigger: 'axis' },legend: { data: ['收入', '支出'], top: '10%' },xAxis: {type: 'category',data: trendData.map(item => item.month)},yAxis: { type: 'value',axisLabel: { formatter: '¥{value}' }},series: [{name: '收入',type: 'line',data: trendData.map(item => item.income),smooth: true,lineStyle: { color: '#4CAF50', width: 3 },areaStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(76, 175, 80, 0.3)' },{ offset: 1, color: 'rgba(76, 175, 80, 0.1)' }])}},{name: '支出', type: 'line',data: trendData.map(item => item.expense),smooth: true,lineStyle: { color: '#f44336', width: 3 },areaStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(244, 67, 54, 0.3)' },{ offset: 1, color: 'rgba(244, 67, 54, 0.1)' }])}}]};trendChart.setOption(option);
}

4. 响应式样式设计 (style.css)

css

.dashboard {max-width: 1200px;margin: 0 auto;background: rgba(255, 255, 255, 0.95);border-radius: 20px;box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);backdrop-filter: blur(10px);
}.stats-cards {display: grid;grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));gap: 20px;padding: 20px;
}.charts-grid {display: grid;grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));gap: 20px;padding: 20px;
}/* 响应式设计 */
@media (max-width: 768px) {.stats-cards {grid-template-columns: 1fr;}.charts-grid {grid-template-columns: 1fr;}
}

 功能详解

1. 数据概览面板

  • 总收入:统计所有正数金额交易

  • 总支出:统计所有负数金额交易的绝对值

  • 当前结余:收入 - 支出的实时计算结果

  • 预算管理:可视化预算使用进度,颜色预警

2. 智能交易记录

  • 灵活金额输入:正数为收入,负数为支出

  • 分类管理:预设9个常用分类

  • 日期选择:支持历史日期记录

  • 备注信息:详细的交易说明

3. 数据可视化

  • 月度趋势图:双线图展示收支变化趋势

  • 消费分类:饼图显示各类别支出比例

  • 实时更新:数据变化后自动刷新图表

4. 预算预警系统

javascript

// 预算进度条颜色根据使用率变化
const expenseRate = (overview.total_expense / overview.budget) * 100;
if (expenseRate > 90) {progressBar.style.background = 'linear-gradient(90deg, #f44336, #ff9800)';
} else if (expenseRate > 70) {progressBar.style.background = 'linear-gradient(90deg, #ff9800, #ffeb3b)';
} else {progressBar.style.background = 'linear-gradient(90deg, #4CAF50, #8BC34A)';
}

 部署运行

环境要求

  • Python 3.6+

  • Flask 2.0+

启动步骤

  1. 安装依赖

bash

pip install flask
  1. 运行应用

bash

python app.py
  1. 访问应用
    打开浏览器访问 http://localhost:5000

 技术亮点

1. 优雅的数据处理

python

# 智能交易类型判断
transaction_data = {'type': '收入' if float(data.get('amount', 0)) > 0 else '支出'
}

2. 实时数据更新

前端使用Fetch API实现无刷新数据交互,用户体验流畅。

3. 自适应图表

ECharts图表自动响应窗口大小变化,确保在各种设备上完美显示。

4. 渐变色彩设计

使用CSS渐变和半透明效果,打造现代化的玻璃拟态界面。

 扩展建议

这个项目还有很大的扩展空间:

  1. 用户认证 - 增加多用户支持

  2. 数据导出 - 支持Excel/PDF导出

  3. 数据备份 - 云存储同步

  4. 智能分析 - 消费习惯分析预测

  5. 移动端APP - 基于Flutter开发移动应用

 项目效果展示

运行项目后,你将看到一个功能完整的财务仪表盘:

  •  美观的渐变界面

  •  实时的数据图表

  •  清晰的收支统计

  •  流畅的交互体验

 总结

通过这个项目,我们实现了一个功能完善、界面美观的个人财务管理系统。项目涵盖了:

  •  Flask后端API开发

  •  ECharts数据可视化

  •  响应式前端设计

  •  JSON数据持久化

  •  RESTful接口设计

各部分完整代码

script.js

// 初始化图表
let trendChart, categoryChart;// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {initCharts();loadDashboardData();setupEventListeners();// 设置默认日期为今天document.getElementById('transaction-date').value = new Date().toISOString().split('T')[0];
});function initCharts() {trendChart = echarts.init(document.getElementById('trend-chart'));categoryChart = echarts.init(document.getElementById('category-chart'));
}function setupEventListeners() {// 表单提交事件document.getElementById('add-transaction-form').addEventListener('submit', function(e) {e.preventDefault();addNewTransaction();});
}async function loadDashboardData() {try {showLoading();const response = await fetch('/api/dashboard-data');const data = await response.json();updateStats(data.overview);renderTrendChart(data.monthly_trend);renderCategoryChart(data.category_data);renderTransactions(data.recent_transactions);hideLoading();} catch (error) {console.error('加载数据失败:', error);hideLoading();alert('数据加载失败,请刷新页面重试');}
}function updateStats(overview) {document.getElementById('total-income').textContent = `¥${overview.total_income.toLocaleString()}`;document.getElementById('total-expense').textContent = `¥${overview.total_expense.toLocaleString()}`;document.getElementById('balance').textContent = `¥${overview.balance.toLocaleString()}`;document.getElementById('budget').textContent = `¥${overview.budget.toLocaleString()}`;const expenseRate = (overview.total_expense / overview.budget) * 100;const progressBar = document.getElementById('budget-progress');progressBar.style.width = `${Math.min(expenseRate, 100)}%`;// 根据预算使用情况改变颜色if (expenseRate > 90) {progressBar.style.background = 'linear-gradient(90deg, #f44336, #ff9800)';} else if (expenseRate > 70) {progressBar.style.background = 'linear-gradient(90deg, #ff9800, #ffeb3b)';} else {progressBar.style.background = 'linear-gradient(90deg, #4CAF50, #8BC34A)';}
}function renderTrendChart(trendData) {const option = {title: { text: '月度收支趋势', left: 'center',textStyle: { fontSize: 16, fontWeight: 'bold' }},tooltip: { trigger: 'axis',formatter: function(params) {let result = `${params[0].axisValue}<br>`;params.forEach(param => {result += `${param.seriesName}: ¥${param.value.toLocaleString()}<br>`;});return result;}},legend: { data: ['收入', '支出'], top: '10%' },grid: { top: '20%', bottom: '15%',left: '3%',right: '3%',containLabel: true},xAxis: {type: 'category',data: trendData.map(item => item.month)},yAxis: { type: 'value',axisLabel: {formatter: '¥{value}'}},series: [{name: '收入',type: 'line',data: trendData.map(item => item.income),smooth: true,lineStyle: { color: '#4CAF50',width: 3},itemStyle: { color: '#4CAF50' },areaStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(76, 175, 80, 0.3)' },{ offset: 1, color: 'rgba(76, 175, 80, 0.1)' }])}},{name: '支出',type: 'line',data: trendData.map(item => item.expense),smooth: true,lineStyle: { color: '#f44336',width: 3},itemStyle: { color: '#f44336' },areaStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(244, 67, 54, 0.3)' },{ offset: 1, color: 'rgba(244, 67, 54, 0.1)' }])}}]};trendChart.setOption(option);
}function renderCategoryChart(categoryData) {const option = {title: { text: '消费分类', left: 'center',textStyle: { fontSize: 16, fontWeight: 'bold' }},tooltip: { trigger: 'item',formatter: '{a} <br/>{b}: ¥{c} ({d}%)'},legend: { orient: 'vertical', left: 'left', top: '10%' },series: [{name: '消费金额',type: 'pie',radius: ['40%', '70%'],avoidLabelOverlap: false,itemStyle: {borderRadius: 10,borderColor: '#fff',borderWidth: 2},label: {show: false,position: 'center'},emphasis: {label: {show: true,fontSize: '18',fontWeight: 'bold'}},labelLine: {show: false},data: categoryData}]};categoryChart.setOption(option);
}function renderTransactions(transactions) {const tbody = document.getElementById('transactions-body');if (transactions.length === 0) {tbody.innerHTML = '<tr><td colspan="5" style="text-align: center; padding: 40px;">暂无交易记录</td></tr>';return;}tbody.innerHTML = transactions.map(transaction => `<tr><td>${transaction.date}</td><td>${transaction.category}</td><td style="color: ${transaction.amount > 0 ? '#4CAF50' : '#f44336'}; font-weight: bold;">${transaction.amount > 0 ? '+' : ''}¥${Math.abs(transaction.amount).toLocaleString()}</td><td><span class="${transaction.type === '收入' ? 'income-badge' : 'expense-badge'}">${transaction.type}</span></td><td>${transaction.note || '-'}</td></tr>`).join('');
}async function addNewTransaction() {const form = document.getElementById('add-transaction-form');const formData = new FormData(form);const transactionData = {date: document.getElementById('transaction-date').value,category: document.getElementById('transaction-category').value,amount: parseFloat(document.getElementById('transaction-amount').value),note: document.getElementById('transaction-note').value};try {const response = await fetch('/api/add-transaction', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify(transactionData)});const result = await response.json();if (result.status === 'success') {alert('记录添加成功!');form.reset();document.getElementById('transaction-date').value = new Date().toISOString().split('T')[0];loadDashboardData(); // 重新加载数据} else {alert('添加失败:' + result.message);}} catch (error) {console.error('添加记录失败:', error);alert('网络错误,请重试');}
}function showBudgetModal() {document.getElementById('budget-modal').style.display = 'block';
}function hideBudgetModal() {document.getElementById('budget-modal').style.display = 'none';
}async function updateBudget() {const newBudget = parseFloat(document.getElementById('new-budget').value);if (!newBudget || newBudget <= 0) {alert('请输入有效的预算金额');return;}try {const response = await fetch('/api/update-budget', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({ budget: newBudget })});const result = await response.json();if (result.status === 'success') {alert('预算更新成功!');hideBudgetModal();loadDashboardData(); // 重新加载数据} else {alert('更新失败:' + result.message);}} catch (error) {console.error('更新预算失败:', error);alert('网络错误,请重试');}
}function showLoading() {// 可以在这里添加加载指示器console.log('加载中...');
}function hideLoading() {// 隐藏加载指示器console.log('加载完成');
}// 响应窗口大小变化
window.addEventListener('resize', function() {trendChart.resize();categoryChart.resize();
});

style.css

* {margin: 0;padding: 0;box-sizing: border-box;
}body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);color: #333;min-height: 100vh;padding: 20px;
}.dashboard {max-width: 1200px;margin: 0 auto;background: rgba(255, 255, 255, 0.95);border-radius: 20px;box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);backdrop-filter: blur(10px);overflow: hidden;
}.header {background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);color: white;padding: 30px;text-align: center;
}.header h1 {font-size: 2.5em;margin-bottom: 10px;
}.stats-cards {display: grid;grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));gap: 20px;padding: 20px;
}.stat-card {background: white;padding: 25px;border-radius: 15px;box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);text-align: center;transition: transform 0.3s ease;position: relative;
}.stat-card:hover {transform: translateY(-5px);
}.stat-card.income { border-left: 5px solid #4CAF50; }
.stat-card.expense { border-left: 5px solid #f44336; }
.stat-card.balance { border-left: 5px solid #2196F3; }
.stat-card.budget { border-left: 5px solid #FF9800; }.stat-number {font-size: 2em;font-weight: bold;margin: 10px 0;
}.add-transaction-section {background: white;margin: 20px;padding: 25px;border-radius: 15px;box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}.transaction-form {display: grid;grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));gap: 15px;margin-top: 15px;
}.form-group {display: flex;flex-direction: column;
}.form-group label {margin-bottom: 5px;font-weight: 600;
}.form-group input,
.form-group select {padding: 10px;border: 1px solid #ddd;border-radius: 8px;font-size: 14px;
}.submit-btn {background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);color: white;border: none;padding: 12px 20px;border-radius: 8px;cursor: pointer;font-size: 16px;align-self: end;
}.submit-btn:hover {opacity: 0.9;
}.charts-grid {display: grid;grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));gap: 20px;padding: 20px;
}.chart-container {background: white;padding: 20px;border-radius: 15px;box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);height: 400px;
}.transactions {padding: 20px;
}.transaction-table {width: 100%;background: white;border-radius: 15px;overflow: hidden;box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}.transaction-table table {width: 100%;border-collapse: collapse;
}.transaction-table th,
.transaction-table td {padding: 15px;text-align: left;border-bottom: 1px solid #eee;
}.transaction-table th {background: #f8f9fa;font-weight: 600;
}.income-badge {background: #4CAF50;color: white;padding: 4px 8px;border-radius: 12px;font-size: 0.8em;
}.expense-badge {background: #f44336;color: white;padding: 4px 8px;border-radius: 12px;font-size: 0.8em;
}.progress-bar {width: 100%;height: 10px;background: #e0e0e0;border-radius: 5px;overflow: hidden;margin: 10px 0;
}.progress-fill {height: 100%;background: linear-gradient(90deg, #4CAF50, #8BC34A);transition: width 0.3s ease;
}.edit-budget-btn {background: #FF9800;color: white;border: none;padding: 8px 15px;border-radius: 5px;cursor: pointer;margin-top: 10px;font-size: 12px;
}.edit-budget-btn:hover {opacity: 0.8;
}/* 模态框样式 */
.modal {display: none;position: fixed;z-index: 1000;left: 0;top: 0;width: 100%;height: 100%;background-color: rgba(0,0,0,0.5);
}.modal-content {background-color: white;margin: 15% auto;padding: 30px;border-radius: 15px;width: 300px;text-align: center;
}.close {color: #aaa;float: right;font-size: 28px;font-weight: bold;cursor: pointer;
}.close:hover {color: black;
}.modal-content input {width: 100%;padding: 10px;margin: 15px 0;border: 1px solid #ddd;border-radius: 8px;
}.modal-content button {background: #FF9800;color: white;border: none;padding: 10px 20px;border-radius: 8px;cursor: pointer;
}/* 响应式设计 */
@media (max-width: 768px) {.stats-cards {grid-template-columns: 1fr;}.charts-grid {grid-template-columns: 1fr;}.transaction-form {grid-template-columns: 1fr;}.header h1 {font-size: 2em;}
}

dashboard.html

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>个人财务仪表盘</title><script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script><link rel="stylesheet" href="/static/style.css">
</head>
<body><div class="dashboard"><!-- 头部 --><div class="header"><h1>💰 个人财务仪表盘</h1><p>清晰掌握每一笔收支,智能分析消费习惯</p></div><!-- 统计卡片 --><div class="stats-cards"><div class="stat-card income"><h3>总收入</h3><div class="stat-number" id="total-income">¥0</div><p>本月累计收入</p></div><div class="stat-card expense"><h3>总支出</h3><div class="stat-number" id="total-expense">¥0</div><p>本月累计支出</p></div><div class="stat-card balance"><h3>当前结余</h3><div class="stat-number" id="balance">¥0</div><p>可用资金</p></div><div class="stat-card budget"><h3>月度预算</h3><div class="stat-number" id="budget">¥0</div><div class="progress-bar"><div class="progress-fill" id="budget-progress"></div></div><button class="edit-budget-btn" onclick="showBudgetModal()">编辑预算</button></div></div><!-- 添加交易表单 --><div class="add-transaction-section"><h3>添加新交易</h3><form id="add-transaction-form" class="transaction-form"><div class="form-group"><label>日期:</label><input type="date" id="transaction-date" required></div><div class="form-group"><label>分类:</label><select id="transaction-category" required><option value="餐饮">餐饮</option><option value="购物">购物</option><option value="交通">交通</option><option value="娱乐">娱乐</option><option value="学习">学习</option><option value="医疗">医疗</option><option value="工资">工资</option><option value="投资">投资</option><option value="其他">其他</option></select></div><div class="form-group"><label>金额:</label><input type="number" id="transaction-amount" step="0.01" placeholder="正数为收入,负数为支出" required></div><div class="form-group"><label>备注:</label><input type="text" id="transaction-note" placeholder="交易说明"></div><button type="submit" class="submit-btn">添加记录</button></form></div><!-- 图表区域 --><div class="charts-grid"><div class="chart-container"><div id="trend-chart" style="width: 100%; height: 100%;"></div></div><div class="chart-container"><div id="category-chart" style="width: 100%; height: 100%;"></div></div></div><!-- 交易记录 --><div class="transactions"><h2>最近交易记录</h2><div class="transaction-table"><table><thead><tr><th>日期</th><th>分类</th><th>金额</th><th>类型</th><th>备注</th></tr></thead><tbody id="transactions-body"><!-- 动态加载 --></tbody></table></div></div></div><!-- 预算编辑模态框 --><div id="budget-modal" class="modal"><div class="modal-content"><span class="close" onclick="hideBudgetModal()">&times;</span><h3>编辑月度预算</h3><input type="number" id="new-budget" placeholder="输入新的预算金额"><button onclick="updateBudget()">更新预算</button></div></div><script src="/static/script.js"></script>
</body>
</html>

app.py

from flask import Flask, jsonify, render_template, request, send_from_directory
import json
import os
from datetime import datetime, timedelta
import randomapp = Flask(__name__)# 数据文件路径
DATA_FILE = 'data.json'def init_data_file():"""初始化数据文件"""if not os.path.exists(DATA_FILE):sample_data = {"transactions": [{"id": 1,"date": "2024-03-20","category": "餐饮","amount": -85,"type": "支出","note": "午餐"},{"id": 2,"date": "2024-03-19","category": "工资","amount": 15000,"type": "收入","note": "月度工资"}],"budget": 10000}save_data(sample_data)def load_data():"""加载数据"""try:with open(DATA_FILE, 'r', encoding='utf-8') as f:return json.load(f)except:return {"transactions": [], "budget": 10000}def save_data(data):"""保存数据"""with open(DATA_FILE, 'w', encoding='utf-8') as f:json.dump(data, f, ensure_ascii=False, indent=2)def calculate_overview(transactions, budget):"""计算概览数据"""total_income = sum(t['amount'] for t in transactions if t['amount'] > 0)total_expense = abs(sum(t['amount'] for t in transactions if t['amount'] < 0))balance = total_income - total_expensereturn {'total_income': total_income,'total_expense': total_expense,'balance': balance,'budget': budget}def generate_monthly_trend(transactions):"""生成月度趋势数据"""# 简化处理:只显示最近4个月months = ['12月', '1月', '2月', '3月']monthly_data = []for month in months:# 模拟数据,实际应该根据交易日期计算monthly_data.append({'month': month,'income': random.randint(12000, 16000),'expense': random.randint(6000, 9000)})return monthly_datadef generate_category_data(transactions):"""生成分类数据"""categories = ['餐饮', '购物', '交通', '娱乐', '学习', '医疗', '其他']category_data = []for category in categories:category_expenses = abs(sum(t['amount'] for t in transactionsif t['category'] == category and t['amount'] < 0))if category_expenses > 0:category_data.append({'name': category,'value': category_expenses})# 如果没有数据,生成模拟数据if not category_data:for category in categories:category_data.append({'name': category,'value': random.randint(500, 3000)})return category_data@app.route('/')
def index():return render_template('dashboard.html')@app.route('/api/dashboard-data')
def get_dashboard_data():data = load_data()transactions = data.get('transactions', [])budget = data.get('budget', 10000)dashboard_data = {'overview': calculate_overview(transactions, budget),'monthly_trend': generate_monthly_trend(transactions),'category_data': generate_category_data(transactions),'recent_transactions': sorted(transactions,key=lambda x: x['date'],reverse=True)[:10]  # 最近10条记录}return jsonify(dashboard_data)@app.route('/api/add-transaction', methods=['POST'])
def add_transaction():try:data = request.jsontransaction_data = {'id': int(datetime.now().timestamp()),'date': data.get('date', datetime.now().strftime('%Y-%m-%d')),'category': data.get('category', '其他'),'amount': float(data.get('amount', 0)),'type': '收入' if float(data.get('amount', 0)) > 0 else '支出','note': data.get('note', '')}# 加载现有数据all_data = load_data()all_data['transactions'].append(transaction_data)# 保存数据save_data(all_data)return jsonify({'status': 'success', 'message': '记录添加成功', 'data': transaction_data})except Exception as e:return jsonify({'status': 'error', 'message': str(e)}), 500@app.route('/api/update-budget', methods=['POST'])
def update_budget():try:data = request.jsonnew_budget = float(data.get('budget', 10000))all_data = load_data()all_data['budget'] = new_budgetsave_data(all_data)return jsonify({'status': 'success', 'message': '预算更新成功', 'budget': new_budget})except Exception as e:return jsonify({'status': 'error', 'message': str(e)}), 500@app.route('/static/<path:filename>')
def static_files(filename):return send_from_directory('static', filename)if __name__ == '__main__':init_data_file()app.run(debug=True, host='0.0.0.0', port=5000)

data.json(存放收支数据)

{"transactions": [{"id": 1,"date": "2024-03-20","category": "餐饮","amount": -85,"type": "支出","note": "午餐"},{"id": 2,"date": "2024-03-19","category": "工资","amount": 15000,"type": "收入","note": "月度工资"},{"id": 1762331018,"date": "2025-11-05","category": "餐饮","amount": 1500.0,"type": "收入","note": ""},{"id": 1763093234,"date": "2025-11-14","category": "餐饮","amount": -12.0,"type": "支出","note": ""}],"budget": 10000
}

完整代码已在文中提供,欢迎大家下载学习、二次开发!!!

http://www.dtcms.com/a/618157.html

相关文章:

  • Galois 理论 | 发展历程 / 基本定理的证明
  • 给定一个数组,如何用最小的比较次数获得最大最小值
  • 个人网站免费源码大全南宁seo管理
  • Linux服务器崩溃急救指南:快速诊断与恢复
  • 后端服务发现配置
  • wordpress建的手机网站合肥信息网
  • 我爱学算法之—— 字符串
  • 关于Function JS加密加密(仅于问题分析)
  • mysql基础——视图
  • win系统做网站wordpress侧边文本轮播图片
  • 免费微商城平台官网一直在做竞价的网站是不是不需要做seo
  • 输出纹波实测:ASP3605在不同输入与负载条件下的稳定性表现
  • RAG向量索引-HNSW Hierarchical Navigable Small World 介绍
  • 沈阳做网站的企业重庆房产网站建设
  • 让老版 IntelliJ IDEA 2020.1.4 支持 JDK 17 启动 springboot3 项目
  • 网站开发逻辑商丘网站建设求职简历
  • [Linux网络——Lesson1.初识计算机网络]
  • 电子电气架构全解析
  • 5G技术:推动数字经济的下一个革命性浪潮
  • 5G与AI赋能智能制造:未来生产的双重驱动力
  • 从工业互联网到智慧城市:5G与物联网的跨界融合
  • 5G NR PBCH与MIB技术介绍
  • 怎么查询网站的点击量招商网站建设全包
  • TCN‑Transformer‑GRU(单输入‑单输出)在 MATLAB 中的实现思路与完整示例代码
  • 重庆市建设工程信息网站诚信分东莞海天网站建设
  • 【Linux】文件操作篇(二):实战理解硬链接与软链接
  • 在RK3568开发板嵌入式开发中,配置NFS服务是实现与Ubuntu虚拟机之间文件共享的常用方法
  • 使用mysql报Communications link failure异常解决
  • 【Linux驱动开发】Linux USB驱动架构详解
  • Linux服务器配置ssh免密登陆多台服务器、服务器别名配置