fix: 修复成员视图展示逻辑错误

This commit is contained in:
yovinchen 2024-12-05 11:26:21 +08:00
parent 70e79ec584
commit 63149f9abb
5 changed files with 92 additions and 28 deletions

View File

@ -108,17 +108,49 @@ interface BookkeepingDao {
@Query("UPDATE bookkeeping_records SET category = :newName WHERE category = :oldName") @Query("UPDATE bookkeeping_records SET category = :newName WHERE category = :oldName")
suspend fun updateRecordCategories(oldName: String, newName: String) suspend fun updateRecordCategories(oldName: String, newName: String)
@Query("""
SELECT * FROM bookkeeping_records
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<BookkeepingRecord>
@Query(""" @Query("""
SELECT * FROM bookkeeping_records SELECT * FROM bookkeeping_records
WHERE memberId IN (SELECT id FROM members WHERE name = :memberName) WHERE memberId IN (SELECT id FROM members WHERE name = :memberName)
AND category = :category AND category = :category
AND date BETWEEN :startDate AND :endDate 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 ORDER BY date DESC
""") """)
suspend fun getRecordsByMemberAndCategory( suspend fun getRecordsByMemberAndCategory(
memberName: String, memberName: String,
category: String, category: String,
startDate: Date, startDate: Date,
endDate: Date endDate: Date,
transactionType: TransactionType?
): List<BookkeepingRecord> ): List<BookkeepingRecord>
} }

View File

@ -22,6 +22,7 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument import androidx.navigation.navArgument
import com.yovinchen.bookkeeping.model.AnalysisType
import com.yovinchen.bookkeeping.model.ThemeMode import com.yovinchen.bookkeeping.model.ThemeMode
import com.yovinchen.bookkeeping.ui.screen.* import com.yovinchen.bookkeeping.ui.screen.*
import java.time.YearMonth import java.time.YearMonth
@ -40,9 +41,9 @@ sealed class Screen(
return "category_detail/$category/${yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"))}" return "category_detail/$category/${yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"))}"
} }
} }
object MemberDetail : Screen("member_detail/{memberName}/{category}/{yearMonth}", "成员详情") { object MemberDetail : Screen("member_detail/{memberName}/{category}/{yearMonth}?type={type}", "成员详情") {
fun createRoute(memberName: String, category: String, yearMonth: YearMonth): String { fun createRoute(memberName: String, category: String, yearMonth: YearMonth, type: AnalysisType): String {
return "member_detail/$memberName/$category/${yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"))}" return "member_detail/$memberName/$category/${yearMonth.format(DateTimeFormatter.ofPattern("yyyy-MM"))}?type=${type.name}"
} }
} }
@ -96,10 +97,8 @@ fun MainNavigation(
onNavigateToCategoryDetail = { category, yearMonth -> onNavigateToCategoryDetail = { category, yearMonth ->
navController.navigate(Screen.CategoryDetail.createRoute(category, yearMonth)) navController.navigate(Screen.CategoryDetail.createRoute(category, yearMonth))
}, },
onNavigateToMemberDetail = { memberName, yearMonth -> onNavigateToMemberDetail = { memberName, yearMonth, analysisType ->
// 在这里我们暂时使用一个默认分类,你需要根据实际情况修改这里的逻辑 navController.navigate(Screen.MemberDetail.createRoute(memberName, "", yearMonth, analysisType))
val defaultCategory = "默认"
navController.navigate(Screen.MemberDetail.createRoute(memberName, defaultCategory, yearMonth))
} }
) )
} }
@ -127,7 +126,7 @@ fun MainNavigation(
yearMonth = yearMonth, yearMonth = yearMonth,
onNavigateBack = { navController.popBackStack() }, onNavigateBack = { navController.popBackStack() },
onNavigateToMemberDetail = { memberName -> 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( arguments = listOf(
navArgument("memberName") { type = NavType.StringType }, navArgument("memberName") { type = NavType.StringType },
navArgument("category") { 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 -> ) { backStackEntry ->
val memberName = backStackEntry.arguments?.getString("memberName") ?: return@composable val memberName = backStackEntry.arguments?.getString("memberName") ?: return@composable
val category = backStackEntry.arguments?.getString("category") ?: return@composable val category = backStackEntry.arguments?.getString("category") ?: return@composable
val yearMonthStr = backStackEntry.arguments?.getString("yearMonth") ?: return@composable val yearMonthStr = backStackEntry.arguments?.getString("yearMonth") ?: return@composable
val yearMonth = YearMonth.parse(yearMonthStr) val yearMonth = YearMonth.parse(yearMonthStr)
val type = backStackEntry.arguments?.getString("type")?.let {
try {
AnalysisType.valueOf(it)
} catch (e: IllegalArgumentException) {
AnalysisType.EXPENSE
}
} ?: AnalysisType.EXPENSE
MemberDetailScreen( MemberDetailScreen(
memberName = memberName, memberName = memberName,
category = category,
yearMonth = yearMonth, yearMonth = yearMonth,
category = category,
analysisType = type,
onNavigateBack = { navController.popBackStack() } onNavigateBack = { navController.popBackStack() }
) )
} }

View File

@ -29,7 +29,7 @@ enum class ViewMode {
@Composable @Composable
fun AnalysisScreen( fun AnalysisScreen(
onNavigateToCategoryDetail: (String, YearMonth) -> Unit, onNavigateToCategoryDetail: (String, YearMonth) -> Unit,
onNavigateToMemberDetail: (String, YearMonth) -> Unit onNavigateToMemberDetail: (String, YearMonth, AnalysisType) -> Unit
) { ) {
val viewModel: AnalysisViewModel = viewModel() val viewModel: AnalysisViewModel = viewModel()
val selectedMonth by viewModel.selectedMonth.collectAsState() val selectedMonth by viewModel.selectedMonth.collectAsState()
@ -138,7 +138,7 @@ fun AnalysisScreen(
if (currentViewMode == ViewMode.CATEGORY) { if (currentViewMode == ViewMode.CATEGORY) {
onNavigateToCategoryDetail(category, selectedMonth) onNavigateToCategoryDetail(category, selectedMonth)
} else { } else {
onNavigateToMemberDetail(category, selectedMonth) onNavigateToMemberDetail(category, selectedMonth, selectedAnalysisType)
} }
} }
) )
@ -153,7 +153,7 @@ fun AnalysisScreen(
if (currentViewMode == ViewMode.CATEGORY) { if (currentViewMode == ViewMode.CATEGORY) {
onNavigateToCategoryDetail(stat.category, selectedMonth) onNavigateToCategoryDetail(stat.category, selectedMonth)
} else { } else {
onNavigateToMemberDetail(stat.category, selectedMonth) onNavigateToMemberDetail(stat.category, selectedMonth, selectedAnalysisType)
} }
} }
) )

View File

@ -25,6 +25,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@ -32,6 +33,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.yovinchen.bookkeeping.data.Record import com.yovinchen.bookkeeping.data.Record
import com.yovinchen.bookkeeping.model.TransactionType import com.yovinchen.bookkeeping.model.TransactionType
import com.yovinchen.bookkeeping.model.AnalysisType
import com.yovinchen.bookkeeping.ui.components.RecordItem import com.yovinchen.bookkeeping.ui.components.RecordItem
import com.yovinchen.bookkeeping.viewmodel.MemberDetailViewModel import com.yovinchen.bookkeeping.viewmodel.MemberDetailViewModel
import java.text.NumberFormat import java.text.NumberFormat
@ -43,16 +45,17 @@ import java.util.Locale
@Composable @Composable
fun MemberDetailScreen( fun MemberDetailScreen(
memberName: String, memberName: String,
category: String,
yearMonth: YearMonth, yearMonth: YearMonth,
category: String = "",
analysisType: AnalysisType = AnalysisType.EXPENSE,
onNavigateBack: () -> Unit, onNavigateBack: () -> Unit,
viewModel: MemberDetailViewModel = viewModel() viewModel: MemberDetailViewModel = viewModel()
) { ) {
val records by viewModel.memberRecords.collectAsState(initial = emptyList()) val records by viewModel.memberRecords.collectAsState(initial = emptyList())
val totalAmount by viewModel.totalAmount.collectAsState(initial = 0.0) val totalAmount by viewModel.totalAmount.collectAsState(initial = 0.0)
LaunchedEffect(memberName, category, yearMonth) { LaunchedEffect(memberName, category, yearMonth, analysisType) {
viewModel.loadMemberRecords(memberName, category, yearMonth) viewModel.loadMemberRecords(memberName, category, yearMonth, analysisType)
} }
val groupedRecords = remember(records) { val groupedRecords = remember(records) {
@ -65,7 +68,7 @@ fun MemberDetailScreen(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { title = {
Text("$category - $memberName") Text(memberName)
}, },
navigationIcon = { navigationIcon = {
IconButton(onClick = onNavigateBack) { IconButton(onClick = onNavigateBack) {

View File

@ -5,6 +5,8 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.yovinchen.bookkeeping.data.BookkeepingDatabase import com.yovinchen.bookkeeping.data.BookkeepingDatabase
import com.yovinchen.bookkeeping.model.BookkeepingRecord 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.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -22,7 +24,7 @@ class MemberDetailViewModel(application: Application) : AndroidViewModel(applica
private val _totalAmount = MutableStateFlow(0.0) private val _totalAmount = MutableStateFlow(0.0)
val totalAmount: StateFlow<Double> = _totalAmount val totalAmount: StateFlow<Double> = _totalAmount
fun loadMemberRecords(memberName: String, category: String, yearMonth: YearMonth) { fun loadMemberRecords(memberName: String, category: String, yearMonth: YearMonth, analysisType: AnalysisType) {
viewModelScope.launch { viewModelScope.launch {
val startDate = yearMonth.atDay(1).atStartOfDay() val startDate = yearMonth.atDay(1).atStartOfDay()
.atZone(ZoneId.systemDefault()) .atZone(ZoneId.systemDefault())
@ -34,12 +36,28 @@ class MemberDetailViewModel(application: Application) : AndroidViewModel(applica
.toInstant() .toInstant()
.let { Date.from(it) } .let { Date.from(it) }
val records = recordDao.getRecordsByMemberAndCategory( val transactionType = when (analysisType) {
memberName = memberName, AnalysisType.INCOME -> TransactionType.INCOME
category = category, AnalysisType.EXPENSE -> TransactionType.EXPENSE
startDate = startDate, else -> null
endDate = endDate }
)
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 _memberRecords.value = records
_totalAmount.value = records.sumOf { it.amount } _totalAmount.value = records.sumOf { it.amount }
} }