package cc.unitmesh.devins.ui.session import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch /** * LoginScreen - 登录界面 * * @param viewModel Session ViewModel * @param onLoginSuccess 登录成功回调 * @param onSkipLogin 跳过登录回调(可选,用于 Android 等平台) * @param showCloseButton 是否显示关闭按钮 */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun LoginScreen( viewModel: SessionViewModel, onLoginSuccess: () -> Unit, onSkipLogin: (() -> Unit)? = null, showCloseButton: Boolean = onSkipLogin != null ) { var username by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } var isRegisterMode by remember { mutableStateOf(false) } var passwordVisible by remember { mutableStateOf(false) } val isLoading by viewModel.isLoading.collectAsState() val errorMessage by viewModel.errorMessage.collectAsState() val scope = rememberCoroutineScope() Scaffold( topBar = { if (showCloseButton && onSkipLogin != null) { TopAppBar( title = { Text("") }, actions = { TextButton(onClick = onSkipLogin) { Text("跳过") } } ) } } ) { paddingValues -> Surface( modifier = Modifier.fillMaxSize().padding(paddingValues), color = MaterialTheme.colorScheme.background ) { Column( modifier = Modifier .fillMaxSize() .padding(32.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { // Logo/Title Text( text = if (isRegisterMode) "创建账号" else "登录", style = MaterialTheme.typography.headlineLarge, color = MaterialTheme.colorScheme.primary ) Spacer(modifier = Modifier.height(8.dp)) Text( text = "AutoDev 多端协同", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) Spacer(modifier = Modifier.height(32.dp)) // Username OutlinedTextField( value = username, onValueChange = { username = it }, label = { Text("用户名") }, singleLine = true, modifier = Modifier.fillMaxWidth().widthIn(max = 400.dp), enabled = !isLoading ) Spacer(modifier = Modifier.height(16.dp)) // Password OutlinedTextField( value = password, onValueChange = { password = it }, label = { Text("密码") }, singleLine = true, visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), trailingIcon = { IconButton(onClick = { passwordVisible = !passwordVisible }) { Icon( imageVector = if (passwordVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff, contentDescription = if (passwordVisible) "隐藏密码" else "显示密码" ) } }, modifier = Modifier.fillMaxWidth().widthIn(max = 400.dp), enabled = !isLoading ) Spacer(modifier = Modifier.height(24.dp)) // Error message if (errorMessage != null) { Text( text = errorMessage ?: "", color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodySmall ) Spacer(modifier = Modifier.height(16.dp)) } // Login/Register Button Button( onClick = { scope.launch { val success = if (isRegisterMode) { viewModel.register(username, password) } else { viewModel.login(username, password) } if (success) { onLoginSuccess() } } }, modifier = Modifier.fillMaxWidth().widthIn(max = 400.dp).height(48.dp), enabled = !isLoading && username.isNotBlank() && password.isNotBlank() ) { if (isLoading) { CircularProgressIndicator( modifier = Modifier.size(24.dp), color = MaterialTheme.colorScheme.onPrimary ) } else { Text(if (isRegisterMode) "注册" else "登录") } } Spacer(modifier = Modifier.height(16.dp)) // Toggle between login and register TextButton( onClick = { isRegisterMode = !isRegisterMode viewModel.clearError() }, enabled = !isLoading ) { Text( if (isRegisterMode) "已有账号?去登录" else "还没账号?去注册" ) } } } } }