1.2.4稳定版 #3
@ -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<BookkeepingRecord>
|
||||
|
||||
@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<BookkeepingRecord>
|
||||
}
|
||||
|
@ -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() }
|
||||
)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -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) {
|
||||
|
@ -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<Double> = _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 }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user