diff --git a/app/src/main/java/com/yovinchen/bookkeeping/data/BookkeepingDao.kt b/app/src/main/java/com/yovinchen/bookkeeping/data/BookkeepingDao.kt index 0e33bae..3bcecb2 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/data/BookkeepingDao.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/data/BookkeepingDao.kt @@ -110,15 +110,47 @@ interface BookkeepingDao { @Query(""" SELECT * FROM bookkeeping_records - WHERE memberId IN (SELECT id FROM members WHERE name = :memberName) - AND category = :category + WHERE memberId IN (SELECT id FROM members WHERE name = :memberName) AND date BETWEEN :startDate AND :endDate + AND ( + :transactionType IS NULL + OR type = ( + CASE :transactionType + WHEN 'INCOME' THEN 'INCOME' + WHEN 'EXPENSE' THEN 'EXPENSE' + END + ) + ) + ORDER BY date DESC + """) + suspend fun getRecordsByMember( + memberName: String, + startDate: Date, + endDate: Date, + transactionType: TransactionType? + ): List + + @Query(""" + SELECT * FROM bookkeeping_records + WHERE memberId IN (SELECT id FROM members WHERE name = :memberName) + AND category = :category + AND date BETWEEN :startDate AND :endDate + AND ( + :transactionType IS NULL + OR type = ( + CASE :transactionType + WHEN 'INCOME' THEN 'INCOME' + WHEN 'EXPENSE' THEN 'EXPENSE' + END + ) + ) ORDER BY date DESC """) suspend fun getRecordsByMemberAndCategory( memberName: String, category: String, startDate: Date, - endDate: Date + endDate: Date, + transactionType: TransactionType? ): List } diff --git a/app/src/main/java/com/yovinchen/bookkeeping/ui/navigation/MainNavigation.kt b/app/src/main/java/com/yovinchen/bookkeeping/ui/navigation/MainNavigation.kt index fd7fe2b..201bf25 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/ui/navigation/MainNavigation.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/ui/navigation/MainNavigation.kt @@ -22,6 +22,7 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument +import com.yovinchen.bookkeeping.model.AnalysisType import com.yovinchen.bookkeeping.model.ThemeMode import com.yovinchen.bookkeeping.ui.screen.* import java.time.YearMonth @@ -40,9 +41,9 @@ sealed class Screen( return "category_detail/$category/${yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"))}" } } - object MemberDetail : Screen("member_detail/{memberName}/{category}/{yearMonth}", "成员详情") { - fun createRoute(memberName: String, category: String, yearMonth: YearMonth): String { - return "member_detail/$memberName/$category/${yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"))}" + object MemberDetail : Screen("member_detail/{memberName}/{category}/{yearMonth}?type={type}", "成员详情") { + fun createRoute(memberName: String, category: String, yearMonth: YearMonth, type: AnalysisType): String { + return "member_detail/$memberName/$category/${yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"))}?type=${type.name}" } } @@ -96,10 +97,8 @@ fun MainNavigation( onNavigateToCategoryDetail = { category, yearMonth -> navController.navigate(Screen.CategoryDetail.createRoute(category, yearMonth)) }, - onNavigateToMemberDetail = { memberName, yearMonth -> - // 在这里我们暂时使用一个默认分类,你需要根据实际情况修改这里的逻辑 - val defaultCategory = "默认" - navController.navigate(Screen.MemberDetail.createRoute(memberName, defaultCategory, yearMonth)) + onNavigateToMemberDetail = { memberName, yearMonth, analysisType -> + navController.navigate(Screen.MemberDetail.createRoute(memberName, "", yearMonth, analysisType)) } ) } @@ -127,7 +126,7 @@ fun MainNavigation( yearMonth = yearMonth, onNavigateBack = { navController.popBackStack() }, onNavigateToMemberDetail = { memberName -> - navController.navigate(Screen.MemberDetail.createRoute(memberName, category, yearMonth)) + navController.navigate(Screen.MemberDetail.createRoute(memberName, category, yearMonth, AnalysisType.EXPENSE)) } ) } @@ -137,18 +136,30 @@ fun MainNavigation( arguments = listOf( navArgument("memberName") { type = NavType.StringType }, navArgument("category") { type = NavType.StringType }, - navArgument("yearMonth") { type = NavType.StringType } + navArgument("yearMonth") { type = NavType.StringType }, + navArgument("type") { + type = NavType.StringType + defaultValue = AnalysisType.EXPENSE.name + } ) ) { backStackEntry -> val memberName = backStackEntry.arguments?.getString("memberName") ?: return@composable val category = backStackEntry.arguments?.getString("category") ?: return@composable val yearMonthStr = backStackEntry.arguments?.getString("yearMonth") ?: return@composable val yearMonth = YearMonth.parse(yearMonthStr) + val type = backStackEntry.arguments?.getString("type")?.let { + try { + AnalysisType.valueOf(it) + } catch (e: IllegalArgumentException) { + AnalysisType.EXPENSE + } + } ?: AnalysisType.EXPENSE MemberDetailScreen( memberName = memberName, - category = category, yearMonth = yearMonth, + category = category, + analysisType = type, onNavigateBack = { navController.popBackStack() } ) } diff --git a/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/AnalysisScreen.kt b/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/AnalysisScreen.kt index 01ffd31..f150084 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/AnalysisScreen.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/AnalysisScreen.kt @@ -29,7 +29,7 @@ enum class ViewMode { @Composable fun AnalysisScreen( onNavigateToCategoryDetail: (String, YearMonth) -> Unit, - onNavigateToMemberDetail: (String, YearMonth) -> Unit + onNavigateToMemberDetail: (String, YearMonth, AnalysisType) -> Unit ) { val viewModel: AnalysisViewModel = viewModel() val selectedMonth by viewModel.selectedMonth.collectAsState() @@ -138,7 +138,7 @@ fun AnalysisScreen( if (currentViewMode == ViewMode.CATEGORY) { onNavigateToCategoryDetail(category, selectedMonth) } else { - onNavigateToMemberDetail(category, selectedMonth) + onNavigateToMemberDetail(category, selectedMonth, selectedAnalysisType) } } ) @@ -149,11 +149,11 @@ fun AnalysisScreen( items(if (currentViewMode == ViewMode.CATEGORY) categoryStats else memberStats) { stat -> CategoryStatItem( stat = stat, - onClick = { + onClick = { if (currentViewMode == ViewMode.CATEGORY) { onNavigateToCategoryDetail(stat.category, selectedMonth) } else { - onNavigateToMemberDetail(stat.category, selectedMonth) + onNavigateToMemberDetail(stat.category, selectedMonth, selectedAnalysisType) } } ) diff --git a/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/MemberDetailScreen.kt b/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/MemberDetailScreen.kt index 896a86f..03af6bd 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/MemberDetailScreen.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/ui/screen/MemberDetailScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight @@ -32,6 +33,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.yovinchen.bookkeeping.data.Record import com.yovinchen.bookkeeping.model.TransactionType +import com.yovinchen.bookkeeping.model.AnalysisType import com.yovinchen.bookkeeping.ui.components.RecordItem import com.yovinchen.bookkeeping.viewmodel.MemberDetailViewModel import java.text.NumberFormat @@ -43,16 +45,17 @@ import java.util.Locale @Composable fun MemberDetailScreen( memberName: String, - category: String, yearMonth: YearMonth, + category: String = "", + analysisType: AnalysisType = AnalysisType.EXPENSE, onNavigateBack: () -> Unit, viewModel: MemberDetailViewModel = viewModel() ) { val records by viewModel.memberRecords.collectAsState(initial = emptyList()) val totalAmount by viewModel.totalAmount.collectAsState(initial = 0.0) - LaunchedEffect(memberName, category, yearMonth) { - viewModel.loadMemberRecords(memberName, category, yearMonth) + LaunchedEffect(memberName, category, yearMonth, analysisType) { + viewModel.loadMemberRecords(memberName, category, yearMonth, analysisType) } val groupedRecords = remember(records) { @@ -65,7 +68,7 @@ fun MemberDetailScreen( topBar = { TopAppBar( title = { - Text("$category - $memberName") + Text(memberName) }, navigationIcon = { IconButton(onClick = onNavigateBack) { diff --git a/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/MemberDetailViewModel.kt b/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/MemberDetailViewModel.kt index 939168b..c623216 100644 --- a/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/MemberDetailViewModel.kt +++ b/app/src/main/java/com/yovinchen/bookkeeping/viewmodel/MemberDetailViewModel.kt @@ -5,6 +5,8 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.yovinchen.bookkeeping.data.BookkeepingDatabase import com.yovinchen.bookkeeping.model.BookkeepingRecord +import com.yovinchen.bookkeeping.model.AnalysisType +import com.yovinchen.bookkeeping.model.TransactionType import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -22,7 +24,7 @@ class MemberDetailViewModel(application: Application) : AndroidViewModel(applica private val _totalAmount = MutableStateFlow(0.0) val totalAmount: StateFlow = _totalAmount - fun loadMemberRecords(memberName: String, category: String, yearMonth: YearMonth) { + fun loadMemberRecords(memberName: String, category: String, yearMonth: YearMonth, analysisType: AnalysisType) { viewModelScope.launch { val startDate = yearMonth.atDay(1).atStartOfDay() .atZone(ZoneId.systemDefault()) @@ -34,12 +36,28 @@ class MemberDetailViewModel(application: Application) : AndroidViewModel(applica .toInstant() .let { Date.from(it) } - val records = recordDao.getRecordsByMemberAndCategory( - memberName = memberName, - category = category, - startDate = startDate, - endDate = endDate - ) + val transactionType = when (analysisType) { + AnalysisType.INCOME -> TransactionType.INCOME + AnalysisType.EXPENSE -> TransactionType.EXPENSE + else -> null + } + + val records = if (category.isEmpty()) { + recordDao.getRecordsByMember( + memberName = memberName, + startDate = startDate, + endDate = endDate, + transactionType = transactionType + ) + } else { + recordDao.getRecordsByMemberAndCategory( + memberName = memberName, + category = category, + startDate = startDate, + endDate = endDate, + transactionType = transactionType + ) + } _memberRecords.value = records _totalAmount.value = records.sumOf { it.amount } }