Compare commits
11 Commits
v1.1.0
...
feature/me
Author | SHA1 | Date | |
---|---|---|---|
1ab75f4701 | |||
773c155d0c | |||
3ad8cf9184 | |||
1147bc47d7 | |||
30e9345d81 | |||
c75439d15a | |||
95b3233d5e | |||
df80dadfea | |||
e03149377c | |||
49e83cea90 | |||
6d9c5a27f7 |
148
README.md
148
README.md
@@ -2,58 +2,136 @@
|
|||||||
|
|
||||||
一个轻量级的个人记账应用,专注于隐私和离线使用。
|
一个轻量级的个人记账应用,专注于隐私和离线使用。
|
||||||
|
|
||||||
## 🌟 特点
|
## 📖 项目概述
|
||||||
|
|
||||||
|
本项目是一个使用 Kotlin 和 Jetpack Compose 开发的 Android 记账应用,采用 MVVM 架构,提供简洁直观的用户界面和丰富的记账功能。
|
||||||
|
|
||||||
|
## ⭐️ 主要特性
|
||||||
|
|
||||||
- 🔒 完全离线运行,无需网络连接
|
- 🔒 完全离线运行,无需网络连接
|
||||||
- 📱 极简权限要求,仅使用必要的系统权限
|
- 📱 极简权限要求,仅使用必要的系统权限
|
||||||
- 💰 支持收入和支出记录
|
- 💰 支持收入和支出记录
|
||||||
- 👥 支持多人记账
|
- 👥 支持多人记账
|
||||||
- 📊 按日期和类别统计
|
- 📊 按日期和类别统计
|
||||||
- 🎨 Material You 设计风格
|
|
||||||
|
|
||||||
## 🛠 技术栈
|
## 🛠 技术栈
|
||||||
|
|
||||||
- 语言:Kotlin
|
- 💻 开发语言:Kotlin
|
||||||
- UI框架:Jetpack Compose
|
- 🎨 UI 框架:Jetpack Compose
|
||||||
- 数据库:Room
|
- 🏗️ 架构模式:MVVM
|
||||||
- 架构:MVVM
|
- 💾 数据存储:Room Database
|
||||||
|
- 💉 依赖注入:Hilt
|
||||||
|
- ⚡️ 异步处理:Kotlin Coroutines
|
||||||
|
|
||||||
## 📱 功能
|
## 🗺 开发路线图
|
||||||
|
|
||||||
### 记账管理
|
### 1. 基础记账 (已完成 ✨)
|
||||||
- 收入和支出记录
|
- [x] 收入/支出记录管理
|
||||||
- 自定义分类管理
|
- [x] 分类管理系统
|
||||||
- 日期和时间选择
|
- [x] 自定义日期选择器
|
||||||
- 备注说明
|
- [x] Material 3 设计界面
|
||||||
|
- [x] 深色/浅色主题切换
|
||||||
|
- [x] 主题色自定义
|
||||||
|
|
||||||
### 成员管理
|
### 2. 成员系统 (已完成 🎉)
|
||||||
- 多人记账支持
|
- [x] 成员添加/编辑/删除
|
||||||
- 成员关联记录
|
- [x] 记账时选择相关成员
|
||||||
- 按成员筛选统计
|
- [x] 主页账单修改相关成员
|
||||||
|
- [x] 成员消费统计
|
||||||
|
|
||||||
### 数据统计
|
### 3. 数据分析 (进行中 🚀)
|
||||||
- 月度收支统计
|
- [ ] 支出/收入趋势图表
|
||||||
- 分类统计
|
- [ ] 分类占比饼图
|
||||||
- 每日收支明细
|
- [ ] 月度/年度报表
|
||||||
|
- [ ] 成员消费分析
|
||||||
|
- [ ] 自定义统计周期
|
||||||
|
|
||||||
## 🔒 隐私保护
|
### 4. 数据管理 (计划中 📝)
|
||||||
|
- [ ] 导出 CSV/Excel 功能
|
||||||
|
- [ ] 云端备份支持
|
||||||
|
- [ ] 数据迁移工具
|
||||||
|
- [ ] 定期自动备份
|
||||||
|
- [ ] 备份加密功能
|
||||||
|
|
||||||
- 完全离线运行,数据存储在本地
|
### 5. 预算管理 (计划中 💡)
|
||||||
- 无需任何网络权限
|
- [ ] 月度预算设置
|
||||||
- 最小化系统权限要求
|
- [ ] 预算超支提醒
|
||||||
|
- [ ] 分类预算管理
|
||||||
|
- [ ] 成员预算管理
|
||||||
|
- [ ] 预算分析报告
|
||||||
|
|
||||||
## 📝 系统要求
|
### 6. 体验优化 (持续进行 🔄)
|
||||||
|
- [x] 深色模式支持
|
||||||
- Android 5.0 (API 21) 或更高版本
|
- [ ] 手势操作优化
|
||||||
- 存储权限(用于数据备份,可选)
|
- [ ] 快速记账小组件
|
||||||
|
- [ ] 多语言支持
|
||||||
## 🔜 未来计划
|
|
||||||
|
|
||||||
- [ ] 数据导出和备份
|
|
||||||
- [ ] 预算管理
|
|
||||||
- [ ] 更多统计图表
|
|
||||||
- [ ] 自定义主题
|
- [ ] 自定义主题
|
||||||
|
|
||||||
|
### 7. 性能提升 (持续进行 ⚡️)
|
||||||
|
- [ ] 大数据量处理优化
|
||||||
|
- [ ] 启动速度优化
|
||||||
|
- [ ] 内存使用优化
|
||||||
|
- [ ] 缓存策略优化
|
||||||
|
- [ ] 数据库查询优化
|
||||||
|
|
||||||
|
## 🌲 分支管理
|
||||||
|
|
||||||
|
- `master`: 稳定主分支
|
||||||
|
- `develop`: 主开发分支
|
||||||
|
- `feature/*`: 功能开发分支
|
||||||
|
- `release/*`: 版本发布分支
|
||||||
|
- `hotfix/*`: 紧急修复分支
|
||||||
|
|
||||||
|
## 📝 版本历史
|
||||||
|
|
||||||
|
### v1.1.0 (2024-01-10)
|
||||||
|
- 成员管理功能
|
||||||
|
- 成员添加/编辑/删除
|
||||||
|
- 记账时选择相关成员
|
||||||
|
- 成员消费统计
|
||||||
|
- UI/UX 优化
|
||||||
|
- 记录展示优化
|
||||||
|
- 月度统计界面
|
||||||
|
- 分组展示优化
|
||||||
|
- 数据管理
|
||||||
|
- 记录筛选增强
|
||||||
|
- 数据库性能优化
|
||||||
|
- 状态管理重构
|
||||||
|
|
||||||
|
### v1.0.0 (2024-01-05)
|
||||||
|
- 基础记账功能
|
||||||
|
- 收入/支出记录
|
||||||
|
- 金额、日期、分类、备注管理
|
||||||
|
- Material 3 设计界面
|
||||||
|
- 深色/浅色主题切换
|
||||||
|
- 主题色自定义
|
||||||
|
- 分类管理
|
||||||
|
- 默认分类预设
|
||||||
|
- 自定义分类支持
|
||||||
|
- 分类编辑与删除
|
||||||
|
- 月度统计
|
||||||
|
- 月度收支总览
|
||||||
|
- 月份快速切换
|
||||||
|
- 自定义日期选择器
|
||||||
|
|
||||||
|
## 🤝 贡献指南
|
||||||
|
|
||||||
|
1. Fork 项目
|
||||||
|
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
|
||||||
|
3. 提交更改 (`git commit -m 'feat: Add some AmazingFeature'`)
|
||||||
|
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||||
|
5. 提交 Pull Request
|
||||||
|
|
||||||
## 📄 许可证
|
## 📄 许可证
|
||||||
|
|
||||||
[MIT License](LICENSE)
|
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详细信息
|
||||||
|
|
||||||
|
## 📮 联系方式
|
||||||
|
|
||||||
|
- 作者:YovinChen
|
||||||
|
- 邮箱:gzh298255@gmail.com
|
||||||
|
- 博客:[blog.hhdxw.top](https://blog.hhdxw.top)
|
||||||
|
|
||||||
|
## 🙏 致谢
|
||||||
|
|
||||||
|
感谢所有为这个项目做出贡献的开发者!
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package com.yovinchen.bookkeeping.ui.components
|
package com.yovinchen.bookkeeping.ui.components
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@@ -22,9 +23,7 @@ import java.time.YearMonth
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MonthYearPickerDialog(
|
fun MonthYearPickerDialog(
|
||||||
selectedMonth: YearMonth,
|
selectedMonth: YearMonth, onMonthSelected: (YearMonth) -> Unit, onDismiss: () -> Unit
|
||||||
onMonthSelected: (YearMonth) -> Unit,
|
|
||||||
onDismiss: () -> Unit
|
|
||||||
) {
|
) {
|
||||||
var currentYearMonth by remember { mutableStateOf(selectedMonth) }
|
var currentYearMonth by remember { mutableStateOf(selectedMonth) }
|
||||||
|
|
||||||
@@ -71,8 +70,7 @@ fun MonthYearPickerDialog(
|
|||||||
|
|
||||||
// 月份网格
|
// 月份网格
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
columns = GridCells.Fixed(3),
|
columns = GridCells.Fixed(3), modifier = Modifier.height(200.dp)
|
||||||
modifier = Modifier.height(200.dp)
|
|
||||||
) {
|
) {
|
||||||
items(12) { index ->
|
items(12) { index ->
|
||||||
val month = index + 1
|
val month = index + 1
|
||||||
@@ -126,6 +124,7 @@ fun MonthYearPickerDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
@Composable
|
@Composable
|
||||||
fun MonthlyStatistics(
|
fun MonthlyStatistics(
|
||||||
totalIncome: Double,
|
totalIncome: Double,
|
||||||
@@ -163,11 +162,9 @@ fun MonthlyStatistics(
|
|||||||
Icon(Icons.AutoMirrored.Filled.KeyboardArrowLeft, "上个月")
|
Icon(Icons.AutoMirrored.Filled.KeyboardArrowLeft, "上个月")
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(text = "${selectedMonth.year}年${selectedMonth.monthValue}月",
|
||||||
text = "${selectedMonth.year}年${selectedMonth.monthValue}月",
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
modifier = Modifier.clickable { showMonthPicker = true }
|
modifier = Modifier.clickable { showMonthPicker = true })
|
||||||
)
|
|
||||||
|
|
||||||
IconButton(onClick = onNextMonth) {
|
IconButton(onClick = onNextMonth) {
|
||||||
Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, "下个月")
|
Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, "下个月")
|
||||||
@@ -177,24 +174,38 @@ fun MonthlyStatistics(
|
|||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
) {
|
||||||
// 收入统计
|
// 支出统计
|
||||||
Column(
|
Column(modifier = Modifier
|
||||||
modifier = Modifier
|
.weight(1f)
|
||||||
.weight(1f)
|
.clickable { onExpenseClick() }
|
||||||
.clickable { onIncomeClick() }
|
.background(
|
||||||
.background(
|
if (selectedType == TransactionType.EXPENSE) MaterialTheme.colorScheme.primaryContainer
|
||||||
if (selectedType == TransactionType.INCOME) MaterialTheme.colorScheme.primaryContainer
|
else Color.Transparent, RoundedCornerShape(8.dp)
|
||||||
else Color.Transparent,
|
)
|
||||||
RoundedCornerShape(8.dp)
|
.padding(8.dp)) {
|
||||||
)
|
|
||||||
.padding(8.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = "收入",
|
text = "支出", style = MaterialTheme.typography.titleMedium
|
||||||
style = MaterialTheme.typography.titleMedium
|
)
|
||||||
|
Text(
|
||||||
|
text = "¥${String.format("%.2f", totalExpense)}",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
// 收入统计
|
||||||
|
Column(modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.clickable { onIncomeClick() }
|
||||||
|
.background(
|
||||||
|
if (selectedType == TransactionType.INCOME) MaterialTheme.colorScheme.primaryContainer
|
||||||
|
else Color.Transparent, RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
.padding(8.dp)) {
|
||||||
|
Text(
|
||||||
|
text = "收入", style = MaterialTheme.typography.titleMedium
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "¥${String.format("%.2f", totalIncome)}",
|
text = "¥${String.format("%.2f", totalIncome)}",
|
||||||
@@ -204,35 +215,30 @@ fun MonthlyStatistics(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
// 结余统计
|
||||||
// 支出统计
|
Column(modifier = Modifier
|
||||||
Column(
|
.weight(1f)
|
||||||
modifier = Modifier
|
.clickable { onClearFilter() }
|
||||||
.weight(1f)
|
.background(
|
||||||
.clickable { onExpenseClick() }
|
if (selectedType == TransactionType.INCOME) MaterialTheme.colorScheme.primaryContainer
|
||||||
.background(
|
else Color.Transparent, RoundedCornerShape(8.dp)
|
||||||
if (selectedType == TransactionType.EXPENSE) MaterialTheme.colorScheme.primaryContainer
|
)
|
||||||
else Color.Transparent,
|
.padding(8.dp)) {
|
||||||
RoundedCornerShape(8.dp)
|
|
||||||
)
|
|
||||||
.padding(8.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = "支出",
|
text = "结余", style = MaterialTheme.typography.titleMedium
|
||||||
style = MaterialTheme.typography.titleMedium
|
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "¥${String.format("%.2f", totalExpense)}",
|
text = "¥${String.format("%.2f", totalIncome - totalExpense)}",
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = MaterialTheme.colorScheme.error
|
color = if (totalIncome >= totalExpense) MaterialTheme.colorScheme.tertiary
|
||||||
|
else MaterialTheme.colorScheme.error
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedType != null) {
|
if (selectedType != null) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = onClearFilter,
|
onClick = onClearFilter, modifier = Modifier.align(Alignment.End)
|
||||||
modifier = Modifier.align(Alignment.End)
|
|
||||||
) {
|
) {
|
||||||
Text("清除筛选")
|
Text("清除筛选")
|
||||||
}
|
}
|
||||||
@@ -241,10 +247,8 @@ fun MonthlyStatistics(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showMonthPicker) {
|
if (showMonthPicker) {
|
||||||
MonthYearPickerDialog(
|
MonthYearPickerDialog(selectedMonth = selectedMonth,
|
||||||
selectedMonth = selectedMonth,
|
|
||||||
onMonthSelected = onMonthSelected,
|
onMonthSelected = onMonthSelected,
|
||||||
onDismiss = { showMonthPicker = false }
|
onDismiss = { showMonthPicker = false })
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user