更新应用导航和现有屏幕

This commit is contained in:
徐在坤
2026-01-17 19:35:01 +08:00
parent cd3a21b208
commit 188946bbad
4 changed files with 938 additions and 240 deletions

View File

@@ -2,32 +2,67 @@ package com.huaga.life_echo.navigation
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.huaga.life_echo.ui.screens.ConversationListScreen
import com.huaga.life_echo.ui.screens.CreateMemoryScreen
import com.huaga.life_echo.ui.screens.ExportDataScreen
import com.huaga.life_echo.ui.screens.MyMemoirScreen
import com.huaga.life_echo.ui.screens.MyOrdersScreen
import com.huaga.life_echo.ui.screens.ProfileScreen
import com.huaga.life_echo.ui.screens.UpgradePlanScreen
sealed class Screen(val route: String) {
object CreateMemory : Screen("create_memory")
object ConversationList : Screen("conversation_list")
object CreateMemory : Screen("create_memory/{conversationId}") {
fun createRoute(conversationId: String = "new") = "create_memory/$conversationId"
}
object MyMemoir : Screen("my_memoir")
object Profile : Screen("profile")
object UpgradePlan : Screen("upgrade_plan")
object MyOrders : Screen("my_orders")
object ExportData : Screen("export_data")
}
@Composable
fun AppNavigation(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = Screen.CreateMemory.route
startDestination = Screen.ConversationList.route
) {
composable(Screen.CreateMemory.route) {
CreateMemoryScreen()
composable(Screen.ConversationList.route) {
ConversationListScreen(
onConversationClick = { conversationId ->
navController.navigate(Screen.CreateMemory.createRoute(conversationId))
}
)
}
composable(
route = Screen.CreateMemory.route,
arguments = listOf(navArgument("conversationId") { type = NavType.StringType })
) { backStackEntry ->
val conversationId = backStackEntry.arguments?.getString("conversationId") ?: "new"
CreateMemoryScreen(
conversationId = conversationId,
navController = navController
)
}
composable(Screen.MyMemoir.route) {
MyMemoirScreen()
MyMemoirScreen(navController = navController)
}
composable(Screen.Profile.route) {
ProfileScreen()
ProfileScreen(navController = navController)
}
composable(Screen.UpgradePlan.route) {
UpgradePlanScreen(navController = navController)
}
composable(Screen.MyOrders.route) {
MyOrdersScreen(navController = navController)
}
composable(Screen.ExportData.route) {
ExportDataScreen(navController = navController)
}
}
}

View File

@@ -1,18 +1,32 @@
package com.huaga.life_echo.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import com.huaga.life_echo.ui.icons.AppIcons
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.huaga.life_echo.ui.theme.LightPurple
import com.huaga.life_echo.ui.viewmodel.CreateMemoryViewModel
import com.huaga.life_echo.ui.viewmodel.ViewModelFactory
import java.text.SimpleDateFormat
import java.util.*
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CreateMemoryScreen(
conversationId: String = "new",
navController: androidx.navigation.NavHostController? = null,
viewModel: CreateMemoryViewModel = viewModel(
factory = ViewModelFactory(LocalContext.current)
)
@@ -25,81 +39,240 @@ fun CreateMemoryScreen(
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
.windowInsetsPadding(WindowInsets.statusBars)
) {
Text(
text = "创建回忆录",
style = MaterialTheme.typography.headlineLarge
)
Text(
text = "连接状态: $connectionStatus",
style = MaterialTheme.typography.bodyMedium
)
// 转写文本显示
Card(
modifier = Modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "你说:",
style = MaterialTheme.typography.labelMedium
)
Text(
text = transcript.ifEmpty { "等待语音输入..." },
style = MaterialTheme.typography.bodyLarge
)
}
}
// Agent 回应显示
Card(
modifier = Modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "AI",
style = MaterialTheme.typography.labelMedium
)
Text(
text = agentResponse.ifEmpty { "等待 AI 回应..." },
style = MaterialTheme.typography.bodyLarge
)
}
}
// 开始/结束按钮
Button(
onClick = {
if (!isRecording) {
viewModel.startConversation()
} else {
viewModel.endConversation()
// 浅紫色标题栏
TopAppBar(
title = {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "回忆录助手",
color = Color.White,
fontSize = 18.sp,
fontWeight = FontWeight.Bold
)
Text(
text = "在线",
color = Color.White,
fontSize = 12.sp
)
}
},
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
) {
Text(
text = if (isRecording) "🟥 结束聊天" else "🎙️ 开始聊天",
style = MaterialTheme.typography.titleLarge
navigationIcon = {
IconButton(onClick = {
navController?.popBackStack()
}) {
Icon(
imageVector = AppIcons.ArrowBack,
contentDescription = "返回",
tint = Color.White
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = LightPurple
)
)
// 聊天内容区域
Column(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.weight(1f)
) {
// 时间戳
Text(
text = "今天 ${SimpleDateFormat("HH:mm", Locale.getDefault()).format(Date())}",
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodySmall
)
// AI助手消息气泡
if (agentResponse.isEmpty() && transcript.isEmpty()) {
// 初始欢迎消息
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
// 书本图标
Box(
modifier = Modifier
.size(32.dp)
.clip(RoundedCornerShape(8.dp))
.background(LightPurple),
contentAlignment = Alignment.Center
) {
Text("📖", fontSize = 20.sp)
}
Spacer(modifier = Modifier.width(8.dp))
// 消息气泡
Card(
modifier = Modifier
.weight(1f)
.shadow(2.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
) {
Column(
modifier = Modifier.padding(12.dp)
) {
Text(
text = "您好!我是您的回忆录助手,很高兴能陪您聊聊往事。😊",
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "您想从哪里开始呢?可以聊聊童年、上学时光,或者任何您想分享的故事。",
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurface
)
}
}
}
}
// 用户消息(转写文本)
if (transcript.isNotEmpty()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
horizontalArrangement = Arrangement.End
) {
Card(
modifier = Modifier
.weight(1f)
.shadow(2.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = LightPurple.copy(alpha = 0.2f))
) {
Text(
text = transcript,
modifier = Modifier.padding(12.dp),
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurface
)
}
}
}
// AI回应消息
if (agentResponse.isNotEmpty()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
Box(
modifier = Modifier
.size(32.dp)
.clip(RoundedCornerShape(8.dp))
.background(LightPurple),
contentAlignment = Alignment.Center
) {
Text("📖", fontSize = 20.sp)
}
Spacer(modifier = Modifier.width(8.dp))
Card(
modifier = Modifier
.weight(1f)
.shadow(2.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
) {
Text(
text = agentResponse,
modifier = Modifier.padding(12.dp),
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurface
)
}
}
}
}
Text(
text = "你可以一直说,我会认真听",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
// 底部输入栏
Surface(
modifier = Modifier
.fillMaxWidth()
.shadow(4.dp),
color = MaterialTheme.colorScheme.surface,
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 麦克风图标
IconButton(
onClick = {
if (!isRecording) {
viewModel.startConversation()
} else {
viewModel.endConversation()
}
}
) {
Icon(
imageVector = if (isRecording) AppIcons.MicOff else AppIcons.Mic,
contentDescription = "语音输入",
tint = if (isRecording) LightPurple else MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(24.dp)
)
}
// 语音输入提示区域(只读,不支持文本输入)
Box(
modifier = Modifier
.weight(1f)
.height(40.dp)
.clip(RoundedCornerShape(20.dp))
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)),
contentAlignment = Alignment.CenterStart
) {
Text(
text = if (isRecording) "正在录音..." else "说点什么...",
modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontSize = 14.sp
)
}
// 表情图标
IconButton(onClick = { /* TODO: 表情选择 */ }) {
Icon(
imageVector = AppIcons.SentimentSatisfied,
contentDescription = "表情",
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(24.dp)
)
}
// 加号图标
IconButton(onClick = { /* TODO: 更多功能 */ }) {
Icon(
imageVector = AppIcons.Add,
contentDescription = "更多",
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(24.dp)
)
}
}
}
}
}

View File

@@ -1,34 +1,34 @@
package com.huaga.life_echo.ui.screens
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import com.huaga.life_echo.ui.icons.AppIcons
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.huaga.life_echo.data.database.Chapter
import com.huaga.life_echo.ui.theme.LightPurple
import com.huaga.life_echo.ui.viewmodel.MyMemoirViewModel
import com.huaga.life_echo.ui.viewmodel.ViewModelFactory
import java.text.SimpleDateFormat
import java.util.*
@Composable
fun MyMemoirScreen(
navController: androidx.navigation.NavHostController? = null,
viewModel: MyMemoirViewModel = viewModel(
factory = ViewModelFactory(LocalContext.current)
)
@@ -36,105 +36,351 @@ fun MyMemoirScreen(
val chapters by viewModel.chapters.collectAsState(initial = emptyList())
val selectedChapter by viewModel.selectedChapter.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
var selectedTab by remember { mutableStateOf(0) } // 0: 目录, 1: 正在阅读
Column(
modifier = Modifier.fillMaxSize()
) {
// 标题
Text(
text = "我的回忆录",
style = MaterialTheme.typography.headlineLarge,
modifier = Modifier.padding(16.dp)
)
// 顶部标签页
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
TabButton(
text = "目录",
selected = selectedTab == 0,
onClick = { selectedTab = 0 },
modifier = Modifier.padding(end = 24.dp)
)
TabButton(
text = "正在阅读",
selected = selectedTab == 1,
onClick = { selectedTab = 1 }
)
}
if (selectedChapter == null) {
// 目录列表
// 目录视图
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp)
) {
items(chapters) { chapter ->
Card(
// 标题区域
item {
Column(
modifier = Modifier
.fillMaxWidth()
.clickable {
viewModel.selectChapter(chapter)
}
.padding(bottom = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = chapter.title,
style = MaterialTheme.typography.titleMedium
)
Text(
text = "状态: ${chapter.status}",
style = MaterialTheme.typography.bodySmall
)
}
Text(
text = "这一生",
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "我的回忆录",
fontSize = 18.sp,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "更新于 2 分钟前",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
// 章节列表
items(chapters.sortedBy { it.orderIndex }) { chapter ->
ChapterCard(
chapter = chapter,
onClick = { viewModel.selectChapter(chapter) }
)
Spacer(modifier = Modifier.height(12.dp))
}
// 示例章节(如果没有数据)
if (chapters.isEmpty()) {
item {
Text(
text = "暂无章节",
modifier = Modifier.padding(16.dp)
ChapterCard(
chapter = Chapter(
id = "demo1",
title = "童年与家庭",
content = "",
orderIndex = 1,
status = "completed",
updatedAt = System.currentTimeMillis(),
category = "childhood"
),
onClick = { viewModel.selectChapter(chapters.firstOrNull() ?: return@ChapterCard) }
)
Spacer(modifier = Modifier.height(12.dp))
ChapterCard(
chapter = Chapter(
id = "demo2",
title = "上学的日子",
content = "",
orderIndex = 2,
status = "partial",
updatedAt = System.currentTimeMillis(),
category = "education"
),
onClick = { viewModel.selectChapter(chapters.firstOrNull() ?: return@ChapterCard) }
)
Spacer(modifier = Modifier.height(12.dp))
ChapterCard(
chapter = Chapter(
id = "demo3",
title = "工作与事业",
content = "",
orderIndex = 3,
status = "pending",
updatedAt = System.currentTimeMillis(),
category = "career"
),
onClick = { viewModel.selectChapter(chapters.firstOrNull() ?: return@ChapterCard) }
)
Spacer(modifier = Modifier.height(12.dp))
ChapterCard(
chapter = Chapter(
id = "demo4",
title = "爱情与婚姻",
content = "",
orderIndex = 4,
status = "pending",
updatedAt = System.currentTimeMillis(),
category = "family"
),
onClick = { viewModel.selectChapter(chapters.firstOrNull() ?: return@ChapterCard) }
)
}
}
}
} else {
// 章节阅读
Column(
// 章节阅读视图
ChapterReadingScreen(
chapter = selectedChapter!!,
onBack = { viewModel.clearSelection() }
)
}
}
}
@Composable
fun TabButton(
text: String,
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier.clickable { onClick() }
) {
Text(
text = text,
fontSize = 16.sp,
fontWeight = if (selected) FontWeight.Bold else FontWeight.Normal,
color = if (selected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
)
if (selected) {
Spacer(modifier = Modifier.height(4.dp))
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.height(2.dp)
.fillMaxWidth()
.background(LightPurple)
)
}
}
}
@Composable
fun ChapterCard(
chapter: Chapter,
onClick: () -> Unit
) {
val statusText = when (chapter.status) {
"completed" -> "已整理 · 约3页"
"partial" -> "部分整理 · 约2页"
else -> "待补充"
}
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { onClick() }
.shadow(2.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 浅紫色编号背景
Box(
modifier = Modifier
.size(48.dp)
.clip(RoundedCornerShape(8.dp))
.background(LightPurple),
contentAlignment = Alignment.Center
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
TextButton(onClick = {
viewModel.clearSelection()
}) {
Text("返回目录")
}
TextButton(
onClick = {
viewModel.exportPdf(
bookId = "current", // TODO: 获取实际 bookId
onSuccess = { pdfBytes ->
// TODO: 保存或分享 PDF
},
onError = { error ->
// TODO: 显示错误提示
}
)
},
enabled = !isLoading
) {
Text(if (isLoading) "导出中..." else "导出 PDF")
}
}
Text(
text = String.format("%02d", chapter.orderIndex),
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
}
Spacer(modifier = Modifier.width(16.dp))
// 章节信息
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = chapter.title,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = statusText,
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
// 右箭头
Icon(
imageVector = AppIcons.ChevronRight,
contentDescription = "进入",
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(24.dp)
)
}
}
}
@Composable
fun ChapterReadingScreen(
chapter: Chapter,
onBack: () -> Unit
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp)
) {
item {
// 返回按钮
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
.clickable { onBack() },
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = AppIcons.ArrowBack,
contentDescription = "返回",
tint = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "返回目录",
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurface
)
}
Spacer(modifier = Modifier.height(16.dp))
// 章节标题
Text(
text = "第一章",
fontSize = 14.sp,
color = LightPurple,
modifier = Modifier.padding(bottom = 4.dp)
)
Text(
text = chapter.title,
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(bottom = 24.dp)
)
// 正文内容
Text(
text = chapter.content.ifEmpty { getSampleContent(chapter.title) },
fontSize = 16.sp,
lineHeight = 28.sp,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(bottom = 16.dp)
)
// 引用块示例(如果有特定内容)
if (chapter.title.contains("童年") || chapter.title.contains("家庭")) {
QuoteBlock(
text = "\"日子虽然清苦, 但那时候的快乐是最纯粹的。\""
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = selectedChapter!!.title,
style = MaterialTheme.typography.headlineMedium
text = "我出生在一个普通的农村家庭,父母都是勤劳朴实的农民。",
fontSize = 16.sp,
lineHeight = 28.sp,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(bottom = 16.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = selectedChapter!!.content,
style = MaterialTheme.typography.bodyLarge
text = "父亲是村里的木匠,手艺精湛,邻里乡亲都愿意找他帮忙。",
fontSize = 16.sp,
lineHeight = 28.sp,
color = MaterialTheme.colorScheme.onSurface
)
}
}
}
}
@Composable
fun QuoteBlock(text: String) {
Box(
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.background(LightPurple.copy(alpha = 0.2f))
.padding(16.dp)
) {
Text(
text = text,
fontSize = 16.sp,
lineHeight = 28.sp,
color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.Medium
)
}
}
fun getSampleContent(title: String): String {
return when {
title.contains("童年") -> "我出生在一个普通的农村家庭,父母都是勤劳朴实的农民。\n\n父亲是村里的木匠,手艺精湛,邻里乡亲都愿意找他帮忙。"
title.contains("上学") -> "上学的日子总是充满欢声笑语,虽然条件艰苦,但学习的快乐让我忘记了生活的艰辛。"
title.contains("工作") -> "工作是我人生中重要的转折点,让我学会了承担责任,也让我明白了生活的意义。"
title.contains("爱情") -> "爱情是人生中最美好的经历之一,它让我懂得了什么是真正的幸福。"
else -> "这里是章节内容..."
}
}

View File

@@ -1,117 +1,361 @@
package com.huaga.life_echo.ui.screens
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.TextButton
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.huaga.life_echo.ui.icons.AppIcons
import com.huaga.life_echo.ui.settings.AppSettings
import com.huaga.life_echo.ui.theme.LightPurple
@Composable
fun ProfileScreen() {
Column(
fun ProfileScreen(
navController: androidx.navigation.NavHostController? = null
) {
var largeFontMode by remember { mutableStateOf(AppSettings.largeFontMode) }
var darkMode by remember { mutableStateOf(AppSettings.darkMode) }
var speechRate by remember { mutableStateOf(AppSettings.speechRate) }
var showSpeechRateDialog by remember { mutableStateOf(false) }
// 应用设置变化 - 立即更新全局设置
LaunchedEffect(largeFontMode) {
AppSettings.largeFontMode = largeFontMode
}
LaunchedEffect(darkMode) {
AppSettings.darkMode = darkMode
// 触发主题重新组合
}
LaunchedEffect(speechRate) {
AppSettings.speechRate = speechRate
}
// 语速选择对话框
if (showSpeechRateDialog) {
AlertDialog(
onDismissRequest = { showSpeechRateDialog = false },
title = { Text("选择语速") },
text = {
Column {
AppSettings.SpeechRate.entries.forEach { rate ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
speechRate = rate
showSpeechRateDialog = false
}
.padding(vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = speechRate == rate,
onClick = {
speechRate = rate
showSpeechRateDialog = false
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(rate.label)
}
}
}
},
confirmButton = {
TextButton(onClick = { showSpeechRateDialog = false }) {
Text("取消")
}
}
)
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
.background(MaterialTheme.colorScheme.background)
) {
Text(
text = "我的",
style = MaterialTheme.typography.headlineLarge
)
// 账户卡片
Card(
modifier = Modifier.fillMaxWidth()
) {
// 用户信息区域
item {
Column(
modifier = Modifier.padding(16.dp)
modifier = Modifier
.fillMaxWidth()
.windowInsetsPadding(WindowInsets.statusBars)
.padding(top = 16.dp, bottom = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// 用户头像
Box(
modifier = Modifier
.size(80.dp)
.clip(CircleShape)
.background(LightPurple.copy(alpha = 0.2f)),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = AppIcons.Person,
contentDescription = "用户头像",
tint = LightPurple,
modifier = Modifier.size(48.dp)
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "账户信息",
style = MaterialTheme.typography.titleMedium
)
Text(
text = "套餐状态: 免费体验",
style = MaterialTheme.typography.bodyMedium
text = "李明华",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.height(4.dp))
Row(
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(8.dp)
.clip(CircleShape)
.background(LightPurple)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "免费体验版",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
// 套餐与付费
Card(
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "套餐与付费",
style = MaterialTheme.typography.titleMedium
item {
SectionTitle("套餐与付费")
SettingCard {
SettingItem(
icon = AppIcons.Upgrade,
title = "升级套餐",
subtitle = "解锁完整导出与更多功能",
onClick = {
navController?.navigate(com.huaga.life_echo.navigation.Screen.UpgradePlan.route)
}
)
Divider(modifier = Modifier.padding(horizontal = 16.dp))
SettingItem(
icon = AppIcons.Receipt,
title = "我的订单",
onClick = {
navController?.navigate(com.huaga.life_echo.navigation.Screen.MyOrders.route)
}
)
TextButton(onClick = { /* TODO */ }) {
Text("升级套餐")
}
TextButton(onClick = { /* TODO */ }) {
Text("我的订单")
}
}
}
// 数据与隐私
Card(
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "数据与隐私",
style = MaterialTheme.typography.titleMedium
item {
SectionTitle("数据与隐私")
SettingCard {
SettingItem(
icon = AppIcons.FileDownload,
title = "导出所有数据",
onClick = {
navController?.navigate(com.huaga.life_echo.navigation.Screen.ExportData.route)
}
)
TextButton(onClick = { /* TODO */ }) {
Text("导出所有数据")
}
}
}
// 设置
Card(
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "设置",
style = MaterialTheme.typography.titleMedium
item {
SectionTitle("设置")
SettingCard {
SettingItem(
icon = AppIcons.AccessTime,
title = "语速",
subtitle = speechRate.label,
onClick = {
// 显示语速选择对话框
showSpeechRateDialog = true
}
)
Divider(modifier = Modifier.padding(horizontal = 16.dp))
SettingItem(
icon = AppIcons.FormatSize,
title = "大字模式",
trailing = {
Switch(
checked = largeFontMode,
onCheckedChange = { largeFontMode = it },
colors = SwitchDefaults.colors(
checkedThumbColor = Color.White,
checkedTrackColor = LightPurple,
uncheckedThumbColor = Color.White,
uncheckedTrackColor = Color(0xFFE0E0E0)
)
)
},
onClick = { largeFontMode = !largeFontMode }
)
Divider(modifier = Modifier.padding(horizontal = 16.dp))
SettingItem(
icon = AppIcons.Brightness2,
title = "夜间模式",
trailing = {
Switch(
checked = darkMode,
onCheckedChange = {
darkMode = it
// 设置变化会通过LaunchedEffect自动同步到AppSettings
},
colors = SwitchDefaults.colors(
checkedThumbColor = Color.White,
checkedTrackColor = LightPurple,
uncheckedThumbColor = Color.White,
uncheckedTrackColor = Color(0xFFE0E0E0)
)
)
},
onClick = { darkMode = !darkMode }
)
TextButton(onClick = { /* TODO */ }) {
Text("语速设置")
}
TextButton(onClick = { /* TODO */ }) {
Text("字体设置")
}
}
}
// 帮助
Card(
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = "帮助",
style = MaterialTheme.typography.titleMedium
)
TextButton(onClick = { /* TODO */ }) {
Text("常见问题")
}
TextButton(onClick = { /* TODO */ }) {
Text("反馈与客服")
}
}
item {
Spacer(modifier = Modifier.height(32.dp))
}
}
}
@Composable
fun SectionTitle(text: String) {
Text(
text = text,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface
)
}
@Composable
fun SettingCard(content: @Composable ColumnScope.() -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 4.dp),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
) {
Column(
modifier = Modifier.padding(vertical = 8.dp)
) {
content()
}
}
}
@Composable
fun SettingItem(
icon: androidx.compose.ui.graphics.vector.ImageVector,
title: String,
subtitle: String? = null,
trailing: @Composable (() -> Unit)? = null,
onClick: () -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onClick() }
.padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 图标
Box(
modifier = Modifier
.size(40.dp)
.clip(RoundedCornerShape(8.dp))
.background(LightPurple.copy(alpha = 0.1f)),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = icon,
contentDescription = title,
tint = LightPurple,
modifier = Modifier.size(24.dp)
)
}
Spacer(modifier = Modifier.width(12.dp))
// 文本
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = title,
fontSize = 16.sp,
fontWeight = FontWeight.Normal,
color = MaterialTheme.colorScheme.onSurface
)
if (subtitle != null) {
Spacer(modifier = Modifier.height(2.dp))
Text(
text = subtitle,
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
// 尾部内容(箭头或开关)
if (trailing != null) {
trailing()
} else {
Icon(
imageVector = AppIcons.ChevronRight,
contentDescription = "进入",
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(24.dp)
)
}
}
}