package cc.unitmesh.devins.ui.task 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.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import cc.unitmesh.devins.ui.session.StatusBadge import cc.unitmesh.session.Session import kotlinx.coroutines.launch import kotlinx.datetime.Instant import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime /** * TaskListScreen - 任务列表界面 */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun TaskListScreen( viewModel: TaskViewModel, onTaskClick: (Session) -> Unit ) { val currentProject by viewModel.currentProject.collectAsState() val tasks by viewModel.tasks.collectAsState() val isLoading by viewModel.isLoading.collectAsState() val errorMessage by viewModel.errorMessage.collectAsState() val scope = rememberCoroutineScope() var showCreateDialog by remember { mutableStateOf(false) } Scaffold( topBar = { TopAppBar( title = { Column { Text("任务列表") currentProject?.let { Text( text = it.name, style = MaterialTheme.typography.bodySmall ) } } } ) }, floatingActionButton = { if (currentProject != null) { FloatingActionButton( onClick = { showCreateDialog = true }, containerColor = MaterialTheme.colorScheme.primary ) { Icon(Icons.Default.Add, contentDescription = "创建任务") } } } ) { paddingValues -> Column( modifier = Modifier .fillMaxSize() .padding(paddingValues) ) { // Error message errorMessage?.let { error -> Card( colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.errorContainer ), modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Row( modifier = Modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically ) { Text( text = error, color = MaterialTheme.colorScheme.onErrorContainer, style = MaterialTheme.typography.bodySmall, modifier = Modifier.weight(1f) ) TextButton(onClick = { viewModel.clearError() }) { Text("关闭") } } } } // No project selected if (currentProject == null) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text( text = "请先选择一个项目", style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } else if (isLoading) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { CircularProgressIndicator() } } else if (tasks.isEmpty()) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp) ) { Text( text = "还没有任务", style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurfaceVariant ) Text( text = "点击 + 创建新任务", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } } else { LazyColumn( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { items(tasks) { task -> TaskCard( task = task, onClick = { onTaskClick(task) }, onExecute = { viewModel.executeTask(task) onTaskClick(task) } ) } } } } } // Create task dialog if (showCreateDialog) { CreateTaskDialog( viewModel = viewModel, onDismiss = { showCreateDialog = false } ) } } @Composable fun TaskCard( task: Session, onClick: () -> Unit, onExecute: () -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .clickable(onClick = onClick), elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) ) { Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { Column(modifier = Modifier.weight(1f)) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { StatusBadge(task.status) Text( text = formatTimestamp(task.createdAt), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant ) } Spacer(modifier = Modifier.height(8.dp)) Text( text = task.task, style = MaterialTheme.typography.bodyMedium, maxLines = 3 ) } IconButton(onClick = onExecute) { Icon( Icons.Default.PlayArrow, contentDescription = "执行任务", tint = MaterialTheme.colorScheme.primary ) } } } } private fun formatTimestamp(timestamp: Long): String { val instant = Instant.fromEpochMilliseconds(timestamp) val dateTime = instant.toLocalDateTime(TimeZone.currentSystemDefault()) return "${dateTime.year}-${dateTime.monthNumber.toString().padStart(2, '0')}-${dateTime.dayOfMonth.toString().padStart(2, '0')} " + "${dateTime.hour.toString().padStart(2, '0')}:${dateTime.minute.toString().padStart(2, '0')}" }