更新应用导航和现有屏幕
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 -> "这里是章节内容..."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user