|
@@ -0,0 +1,239 @@
|
|
|
|
|
+package com.iscs.bozzys.ui.pages.compose
|
|
|
|
|
+
|
|
|
|
|
+import androidx.compose.foundation.border
|
|
|
|
|
+import androidx.compose.foundation.clickable
|
|
|
|
|
+import androidx.compose.foundation.layout.Box
|
|
|
|
|
+import androidx.compose.foundation.layout.Column
|
|
|
|
|
+import androidx.compose.foundation.layout.FlowRow
|
|
|
|
|
+import androidx.compose.foundation.layout.Row
|
|
|
|
|
+import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
|
|
+import androidx.compose.foundation.layout.height
|
|
|
|
|
+import androidx.compose.foundation.layout.heightIn
|
|
|
|
|
+import androidx.compose.foundation.layout.offset
|
|
|
|
|
+import androidx.compose.foundation.layout.padding
|
|
|
|
|
+import androidx.compose.foundation.layout.size
|
|
|
|
|
+import androidx.compose.foundation.layout.width
|
|
|
|
|
+import androidx.compose.foundation.rememberScrollState
|
|
|
|
|
+import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
|
|
+import androidx.compose.foundation.text.BasicTextField
|
|
|
|
|
+import androidx.compose.foundation.text.KeyboardOptions
|
|
|
|
|
+import androidx.compose.foundation.verticalScroll
|
|
|
|
|
+import androidx.compose.material3.DropdownMenu
|
|
|
|
|
+import androidx.compose.material3.DropdownMenuItem
|
|
|
|
|
+import androidx.compose.material3.Icon
|
|
|
|
|
+import androidx.compose.material3.LocalTextStyle
|
|
|
|
|
+import androidx.compose.material3.RadioButton
|
|
|
|
|
+import androidx.compose.material3.Text
|
|
|
|
|
+import androidx.compose.runtime.Composable
|
|
|
|
|
+import androidx.compose.runtime.getValue
|
|
|
|
|
+import androidx.compose.runtime.mutableStateOf
|
|
|
|
|
+import androidx.compose.runtime.remember
|
|
|
|
|
+import androidx.compose.runtime.setValue
|
|
|
|
|
+import androidx.compose.ui.Alignment
|
|
|
|
|
+import androidx.compose.ui.Modifier
|
|
|
|
|
+import androidx.compose.ui.draw.clip
|
|
|
|
|
+import androidx.compose.ui.draw.rotate
|
|
|
|
|
+import androidx.compose.ui.graphics.Color
|
|
|
|
|
+import androidx.compose.ui.graphics.SolidColor
|
|
|
|
|
+import androidx.compose.ui.layout.onSizeChanged
|
|
|
|
|
+import androidx.compose.ui.platform.LocalDensity
|
|
|
|
|
+import androidx.compose.ui.res.painterResource
|
|
|
|
|
+import androidx.compose.ui.text.font.FontWeight
|
|
|
|
|
+import androidx.compose.ui.text.input.KeyboardType
|
|
|
|
|
+import androidx.compose.ui.unit.DpOffset
|
|
|
|
|
+import androidx.compose.ui.unit.dp
|
|
|
|
|
+import androidx.compose.ui.unit.sp
|
|
|
|
|
+import com.iscs.bozzys.R
|
|
|
|
|
+import com.iscs.bozzys.ui.theme.Main
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 表单输入框
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param label 表单的标题
|
|
|
|
|
+ * @param value 表单的内容
|
|
|
|
|
+ * @param onValueChange 表单内容发生变化
|
|
|
|
|
+ */
|
|
|
|
|
+@Composable
|
|
|
|
|
+fun FormInput(label: String, value: String, onValueChange: (String) -> Unit) {
|
|
|
|
|
+ Column(Modifier.fillMaxWidth()) {
|
|
|
|
|
+ Row(
|
|
|
|
|
+ Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .height(40.dp), verticalAlignment = Alignment.CenterVertically
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Text(label, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
|
|
|
|
+ Text("*", fontSize = 14.sp, color = Color(0xFFFF4D4F), modifier = Modifier.padding(start = 3.dp))
|
|
|
|
|
+ }
|
|
|
|
|
+ BasicTextField(
|
|
|
|
|
+ value,
|
|
|
|
|
+ onValueChange = onValueChange,
|
|
|
|
|
+ Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .height(46.dp)
|
|
|
|
|
+ .border(1.dp, shape = RoundedCornerShape(6.dp), color = Color(0xFFE5E6EB))
|
|
|
|
|
+ .padding(horizontal = 10.dp),
|
|
|
|
|
+ singleLine = true,
|
|
|
|
|
+ textStyle = LocalTextStyle.current.copy(fontSize = 14.sp, lineHeight = 18.sp),
|
|
|
|
|
+ decorationBox = { innerTextField ->
|
|
|
|
|
+ Box(contentAlignment = Alignment.CenterStart) {
|
|
|
|
|
+ innerTextField()
|
|
|
|
|
+ if (value.isEmpty()) {
|
|
|
|
|
+ val text = "请输入$label"
|
|
|
|
|
+ Text(text, color = Color(0xFF9CA3AF), fontSize = 14.sp, lineHeight = 18.sp)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ cursorBrush = SolidColor(Main),
|
|
|
|
|
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 表单选择器
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param label 标题
|
|
|
|
|
+ * @param value 当前选中
|
|
|
|
|
+ * @param options 选择列表
|
|
|
|
|
+ * @param onSelectChange 选择变化监听
|
|
|
|
|
+ */
|
|
|
|
|
+@Composable
|
|
|
|
|
+fun <T> FormSelect(label: String, value: Pair<String, T>, options: List<Pair<String, T>>, onSelectChange: (Pair<String, T>) -> Unit) {
|
|
|
|
|
+ var expanded by remember { mutableStateOf(false) }
|
|
|
|
|
+ var width by remember { mutableStateOf(0) }
|
|
|
|
|
+ val density = LocalDensity.current
|
|
|
|
|
+ Column(
|
|
|
|
|
+ Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .onSizeChanged({ width = it.width })
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Row(
|
|
|
|
|
+ Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .height(40.dp), verticalAlignment = Alignment.CenterVertically
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Text(label, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
|
|
|
|
+ Text("*", fontSize = 14.sp, color = Color(0xFFFF4D4F), modifier = Modifier.padding(start = 3.dp))
|
|
|
|
|
+ }
|
|
|
|
|
+ Row(
|
|
|
|
|
+ Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .height(46.dp)
|
|
|
|
|
+ .border(1.dp, shape = RoundedCornerShape(6.dp), color = Color(0xFFE5E6EB))
|
|
|
|
|
+ .clip(RoundedCornerShape(6.dp))
|
|
|
|
|
+ .clickable(onClick = { expanded = true })
|
|
|
|
|
+ .padding(horizontal = 10.dp),
|
|
|
|
|
+ verticalAlignment = Alignment.CenterVertically
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Text(
|
|
|
|
|
+ value.first,
|
|
|
|
|
+ color = Color.Black,
|
|
|
|
|
+ fontSize = 14.sp,
|
|
|
|
|
+ lineHeight = 18.sp,
|
|
|
|
|
+ modifier = Modifier.weight(1f)
|
|
|
|
|
+ )
|
|
|
|
|
+ Icon(
|
|
|
|
|
+ painter = painterResource(R.drawable.back), contentDescription = "", modifier = Modifier
|
|
|
|
|
+ .rotate(if (expanded) -90f else 180f)
|
|
|
|
|
+ .size(12.dp), tint = Color.Black
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ val widthDp = with(density) { width.toDp() }
|
|
|
|
|
+ DropdownMenu(
|
|
|
|
|
+ expanded = expanded, onDismissRequest = { expanded = false },
|
|
|
|
|
+ modifier = Modifier.width(widthDp),
|
|
|
|
|
+ offset = DpOffset(x = 0.dp, y = 5.dp)
|
|
|
|
|
+ ) {
|
|
|
|
|
+ options.forEach { option ->
|
|
|
|
|
+ DropdownMenuItem(
|
|
|
|
|
+ modifier = Modifier.fillMaxWidth(),
|
|
|
|
|
+ text = { Text(option.first) },
|
|
|
|
|
+ onClick = {
|
|
|
|
|
+ onSelectChange(option)
|
|
|
|
|
+ expanded = false
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 表单多行文本输入
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param label 标题
|
|
|
|
|
+ * @param value 输入的内容
|
|
|
|
|
+ * @param onValueChange 输入内容发生变化
|
|
|
|
|
+ * @param placeholder 占位内容
|
|
|
|
|
+ */
|
|
|
|
|
+@Composable
|
|
|
|
|
+fun FormTextarea(label: String, value: String, onValueChange: (String) -> Unit, placeholder: String = "") {
|
|
|
|
|
+ Column(Modifier.fillMaxWidth()) {
|
|
|
|
|
+ Row(
|
|
|
|
|
+ Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .height(40.dp), verticalAlignment = Alignment.CenterVertically
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Text(label, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
|
|
|
|
+ Text("*", fontSize = 14.sp, color = Color(0xFFFF4D4F), modifier = Modifier.padding(start = 3.dp))
|
|
|
|
|
+ }
|
|
|
|
|
+ BasicTextField(
|
|
|
|
|
+ value,
|
|
|
|
|
+ onValueChange = onValueChange,
|
|
|
|
|
+ Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .heightIn(min = 120.dp)
|
|
|
|
|
+ .border(1.dp, shape = RoundedCornerShape(6.dp), color = Color(0xFFE5E6EB))
|
|
|
|
|
+ .padding(10.dp)
|
|
|
|
|
+ .verticalScroll(rememberScrollState()),
|
|
|
|
|
+ textStyle = LocalTextStyle.current.copy(fontSize = 14.sp, lineHeight = 18.sp),
|
|
|
|
|
+ decorationBox = { innerTextField ->
|
|
|
|
|
+ Box(contentAlignment = Alignment.TopStart) {
|
|
|
|
|
+ innerTextField()
|
|
|
|
|
+ if (value.isEmpty()) {
|
|
|
|
|
+ val text = placeholder.ifEmpty { "请输入$label" }
|
|
|
|
|
+ Text(text, color = Color(0xFF9CA3AF), fontSize = 14.sp, lineHeight = 18.sp)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ cursorBrush = SolidColor(Main),
|
|
|
|
|
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 表单单选组件
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param label 标题
|
|
|
|
|
+ * @param value 当前选中
|
|
|
|
|
+ * @param options 可选列表
|
|
|
|
|
+ * @param onSelectChange 当前选中
|
|
|
|
|
+ */
|
|
|
|
|
+@Composable
|
|
|
|
|
+fun <T> FormRadio(label: String, value: Pair<String, T>, options: List<Pair<String, T>>, onSelectChange: (Pair<String, T>) -> Unit) {
|
|
|
|
|
+ Column(Modifier.fillMaxWidth()) {
|
|
|
|
|
+ Row(
|
|
|
|
|
+ Modifier
|
|
|
|
|
+ .fillMaxWidth()
|
|
|
|
|
+ .height(40.dp), verticalAlignment = Alignment.CenterVertically
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Text(label, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
|
|
|
|
+ Text("*", fontSize = 14.sp, color = Color(0xFFFF4D4F), modifier = Modifier.padding(start = 3.dp))
|
|
|
|
|
+ }
|
|
|
|
|
+ FlowRow(Modifier.fillMaxWidth()) {
|
|
|
|
|
+ options.forEach { item ->
|
|
|
|
|
+ Row(
|
|
|
|
|
+ modifier = Modifier
|
|
|
|
|
+ .clip(RoundedCornerShape(12.dp))
|
|
|
|
|
+ .clickable { onSelectChange(item) }
|
|
|
|
|
+ .padding(horizontal = 10.dp, vertical = 5.dp),
|
|
|
|
|
+ verticalAlignment = Alignment.CenterVertically
|
|
|
|
|
+ ) {
|
|
|
|
|
+ RadioButton(selected = value == item, onClick = null, modifier = Modifier.size(14.dp))
|
|
|
|
|
+ Text(text = item.first, fontSize = 15.sp, modifier = Modifier.padding(start = 10.dp))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|