parent
8e527cf491
commit
671b682711
@ -1,38 +0,0 @@ |
|||||||
package com.gyf.csams |
|
||||||
|
|
||||||
import android.os.Bundle |
|
||||||
import androidx.activity.ComponentActivity |
|
||||||
import androidx.activity.compose.setContent |
|
||||||
import androidx.compose.material.MaterialTheme |
|
||||||
import androidx.compose.material.Surface |
|
||||||
import androidx.compose.material.Text |
|
||||||
import androidx.compose.runtime.Composable |
|
||||||
import androidx.compose.ui.tooling.preview.Preview |
|
||||||
import com.gyf.lib.uikit.theme.CSAMSTheme |
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() { |
|
||||||
override fun onCreate(savedInstanceState: Bundle?) { |
|
||||||
super.onCreate(savedInstanceState) |
|
||||||
setContent { |
|
||||||
CSAMSTheme { |
|
||||||
// A surface container using the 'background' color from the theme |
|
||||||
Surface(color = MaterialTheme.colors.background) { |
|
||||||
Greeting("Android") |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Composable |
|
||||||
fun Greeting(name: String) { |
|
||||||
Text(text = "Hello $name!") |
|
||||||
} |
|
||||||
|
|
||||||
@Preview(showBackground = true) |
|
||||||
@Composable |
|
||||||
fun DefaultPreview() { |
|
||||||
CSAMSTheme { |
|
||||||
Greeting("Android") |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,17 @@ |
|||||||
|
package com.gyf.csams |
||||||
|
|
||||||
|
import android.app.Application |
||||||
|
import com.orhanobut.logger.AndroidLogAdapter |
||||||
|
import com.orhanobut.logger.DiskLogAdapter |
||||||
|
import com.orhanobut.logger.Logger |
||||||
|
|
||||||
|
class MainApplication : Application() { |
||||||
|
override fun onCreate() { |
||||||
|
super.onCreate() |
||||||
|
//初始化日志 |
||||||
|
Logger.addLogAdapter(AndroidLogAdapter()) |
||||||
|
Logger.addLogAdapter(DiskLogAdapter()) |
||||||
|
|
||||||
|
Logger.i("${BuildConfig.background_app_name}启动") |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.gyf.csams.account.model |
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel |
||||||
|
import com.gyf.lib.uikit.StringForm |
||||||
|
|
||||||
|
class LoginViewModel : ViewModel() { |
||||||
|
val user = StringForm(formDesc = "管理帐号", textLength = 8) |
||||||
|
val password = StringForm(formDesc = "管理密码", textLength = 8) |
||||||
|
|
||||||
|
val login = "登录" |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
package com.gyf.csams.account.ui |
||||||
|
|
||||||
|
import android.content.Intent |
||||||
|
import android.os.Bundle |
||||||
|
import androidx.activity.ComponentActivity |
||||||
|
import androidx.activity.compose.setContent |
||||||
|
import androidx.compose.foundation.layout.Arrangement |
||||||
|
import androidx.compose.foundation.layout.Column |
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth |
||||||
|
import androidx.compose.foundation.layout.height |
||||||
|
import androidx.compose.material.OutlinedButton |
||||||
|
import androidx.compose.material.Text |
||||||
|
import androidx.compose.ui.Alignment |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.platform.LocalContext |
||||||
|
import androidx.compose.ui.text.input.PasswordVisualTransformation |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel |
||||||
|
import com.gyf.csams.account.model.LoginViewModel |
||||||
|
import com.gyf.csams.main.ui.MainActivity |
||||||
|
import com.gyf.lib.uikit.BaseTextField |
||||||
|
import com.gyf.lib.uikit.Body |
||||||
|
import com.gyf.lib.uikit.MainBoxFrame |
||||||
|
import com.gyf.lib.uikit.ShowSnackbar |
||||||
|
|
||||||
|
/** |
||||||
|
* 登录 |
||||||
|
* |
||||||
|
*/ |
||||||
|
class LoginActivity : ComponentActivity() { |
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
|
||||||
|
setContent { |
||||||
|
Body { scaffoldState -> |
||||||
|
val context = LocalContext.current as LoginActivity |
||||||
|
MainBoxFrame( |
||||||
|
background = { /*TODO 背景图*/ }, |
||||||
|
contentAlignment = Alignment.Center |
||||||
|
) { |
||||||
|
Column( |
||||||
|
modifier = Modifier |
||||||
|
.fillMaxWidth() |
||||||
|
.height(300.dp), |
||||||
|
verticalArrangement = Arrangement.SpaceEvenly, |
||||||
|
horizontalAlignment = Alignment.CenterHorizontally |
||||||
|
) { |
||||||
|
val model: LoginViewModel = viewModel() |
||||||
|
BaseTextField(form = model.user) |
||||||
|
BaseTextField( |
||||||
|
form = model.password, |
||||||
|
visualTransformation = PasswordVisualTransformation() |
||||||
|
) |
||||||
|
OutlinedButton(onClick = { |
||||||
|
context.startActivity(Intent(context, MainActivity::class.java)) |
||||||
|
context.finish() |
||||||
|
}) { |
||||||
|
Text(text = model.login) |
||||||
|
} |
||||||
|
ShowSnackbar(scaffoldState = scaffoldState) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,4 @@ |
|||||||
|
package com.gyf.csams.main.model |
||||||
|
|
||||||
|
class DepartmentViewModel { |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
package com.gyf.csams.main.model |
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData |
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
import androidx.lifecycle.ViewModel |
||||||
|
import com.gyf.lib.uikit.PersonInfoVo |
||||||
|
import com.gyf.lib.util.randomChinese |
||||||
|
|
||||||
|
/** |
||||||
|
* 部长 |
||||||
|
* |
||||||
|
* @property name 姓名 |
||||||
|
* @property duty 职务 |
||||||
|
* @property headImg 头像 |
||||||
|
* @property desc 个人简介 |
||||||
|
*/ |
||||||
|
data class MinisterVo( |
||||||
|
override val name: String, |
||||||
|
override val duty: String, |
||||||
|
override val headImg: String, |
||||||
|
override val desc: String |
||||||
|
) : PersonInfoVo() |
||||||
|
|
||||||
|
class MainViewModel : ViewModel() { |
||||||
|
private val _person = MutableLiveData<MinisterVo>() |
||||||
|
val person: LiveData<MinisterVo> = _person |
||||||
|
|
||||||
|
init { |
||||||
|
loadInfo() |
||||||
|
} |
||||||
|
|
||||||
|
private fun loadInfo() { |
||||||
|
_person.postValue( |
||||||
|
MinisterVo( |
||||||
|
name = randomChinese(3), |
||||||
|
duty = "总部长", |
||||||
|
headImg = "", |
||||||
|
desc = randomChinese(8) |
||||||
|
) |
||||||
|
) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,172 @@ |
|||||||
|
package com.gyf.csams.main.ui |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.activity.ComponentActivity |
||||||
|
import androidx.activity.compose.setContent |
||||||
|
import androidx.annotation.StringRes |
||||||
|
import androidx.compose.foundation.border |
||||||
|
import androidx.compose.foundation.clickable |
||||||
|
import androidx.compose.foundation.layout.* |
||||||
|
import androidx.compose.material.* |
||||||
|
import androidx.compose.runtime.* |
||||||
|
import androidx.compose.ui.Alignment |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.platform.LocalContext |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import com.gyf.csams.R |
||||||
|
import com.gyf.lib.uikit.Body |
||||||
|
import com.gyf.lib.uikit.MainColumnFrame |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 部门管理 |
||||||
|
* |
||||||
|
*/ |
||||||
|
class DepartmentActivity : ComponentActivity() { |
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
|
||||||
|
setContent { |
||||||
|
Body { _ -> |
||||||
|
|
||||||
|
|
||||||
|
MainColumnFrame(background = { /*TODO*/ }) { |
||||||
|
val weight = 0.1F |
||||||
|
val departWeight = 0.2F |
||||||
|
val space = 0.05F |
||||||
|
val context = LocalContext.current |
||||||
|
var dialogContent: Int? by remember { |
||||||
|
mutableStateOf(null) |
||||||
|
} |
||||||
|
|
||||||
|
dialogContent?.let { |
||||||
|
AlertDialog(onDismissRequest = { /*TODO*/ }, |
||||||
|
text = { |
||||||
|
Card(backgroundColor = MaterialTheme.colors.background) { |
||||||
|
Column { |
||||||
|
Row( |
||||||
|
modifier = Modifier.fillMaxWidth(), |
||||||
|
horizontalArrangement = Arrangement.Center |
||||||
|
) { |
||||||
|
Text(text = context.getString(R.string.department_desc)) |
||||||
|
} |
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(10.dp)) |
||||||
|
Box( |
||||||
|
modifier = Modifier |
||||||
|
.height(300.dp) |
||||||
|
.fillMaxWidth(), contentAlignment = Alignment.Center |
||||||
|
) { |
||||||
|
when (it) { |
||||||
|
R.string.secretariat -> Text( |
||||||
|
text = context.getString( |
||||||
|
R.string.secretariat_desc |
||||||
|
) |
||||||
|
) |
||||||
|
R.string.propaganda_department -> Text( |
||||||
|
text = context.getString( |
||||||
|
R.string.propaganda_desc |
||||||
|
) |
||||||
|
) |
||||||
|
R.string.public_relations_department -> Text( |
||||||
|
text = context.getString( |
||||||
|
R.string.public_relations_department_desc |
||||||
|
) |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, buttons = { |
||||||
|
Row( |
||||||
|
modifier = Modifier.fillMaxWidth(), |
||||||
|
horizontalArrangement = Arrangement.Center |
||||||
|
) { |
||||||
|
OutlinedButton(onClick = { dialogContent = null }) { |
||||||
|
Text(text = context.getString(R.string.close)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
Row( |
||||||
|
modifier = Modifier |
||||||
|
.fillMaxWidth() |
||||||
|
.weight(weight = weight), |
||||||
|
verticalAlignment = Alignment.CenterVertically, |
||||||
|
horizontalArrangement = Arrangement.Center |
||||||
|
) { |
||||||
|
Text(text = context.getString(R.string.department_management)) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DepartmentItem(modifier = Modifier.weight(weight = departWeight), |
||||||
|
id = R.string.secretariat, |
||||||
|
onClick = { dialogContent = R.string.secretariat }) |
||||||
|
Spacer(modifier = Modifier.weight(space)) |
||||||
|
DepartmentItem(modifier = Modifier.weight(weight = departWeight), |
||||||
|
id = R.string.propaganda_department, |
||||||
|
onClick = { dialogContent = R.string.propaganda_department }) |
||||||
|
Spacer(modifier = Modifier.weight(space)) |
||||||
|
DepartmentItem(modifier = Modifier.weight(weight = departWeight), |
||||||
|
id = R.string.public_relations_department, |
||||||
|
onClick = { dialogContent = R.string.public_relations_department }) |
||||||
|
Spacer(modifier = Modifier.weight(space)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun DepartmentItem( |
||||||
|
modifier: Modifier = Modifier, |
||||||
|
@StringRes id: Int, |
||||||
|
onClick: () -> Unit |
||||||
|
) { |
||||||
|
val context = LocalContext.current |
||||||
|
Row( |
||||||
|
modifier = modifier |
||||||
|
.fillMaxWidth() |
||||||
|
.padding(20.dp) |
||||||
|
) { |
||||||
|
Box( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.40F) |
||||||
|
.fillMaxHeight() |
||||||
|
.border( |
||||||
|
width = 1.dp, |
||||||
|
color = MaterialTheme.colors.onBackground |
||||||
|
) |
||||||
|
.clickable(onClick = onClick), |
||||||
|
contentAlignment = Alignment.Center |
||||||
|
) { |
||||||
|
Text(text = context.getString(id)) |
||||||
|
} |
||||||
|
Spacer(modifier = Modifier.weight(0.1F)) |
||||||
|
Column( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.40F) |
||||||
|
.fillMaxHeight(), |
||||||
|
verticalArrangement = Arrangement.SpaceBetween |
||||||
|
) { |
||||||
|
RowItem(text = "部门部长:") |
||||||
|
RowItem(text = "部门干事管理") |
||||||
|
RowItem(text = "部门总人数:") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
private fun RowItem(text: String) { |
||||||
|
Row( |
||||||
|
modifier = Modifier |
||||||
|
.fillMaxWidth() |
||||||
|
.border(width = 1.dp, color = MaterialTheme.colors.onBackground), |
||||||
|
horizontalArrangement = Arrangement.Center |
||||||
|
) { |
||||||
|
Text(text = text) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
package com.gyf.csams.main.ui |
||||||
|
|
||||||
|
import android.content.Intent |
||||||
|
import android.os.Bundle |
||||||
|
import androidx.activity.ComponentActivity |
||||||
|
import androidx.activity.compose.setContent |
||||||
|
import androidx.compose.foundation.layout.Arrangement |
||||||
|
import androidx.compose.foundation.layout.Column |
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth |
||||||
|
import androidx.compose.foundation.layout.padding |
||||||
|
import androidx.compose.material.OutlinedButton |
||||||
|
import androidx.compose.material.Text |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.runtime.livedata.observeAsState |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.platform.LocalContext |
||||||
|
import androidx.compose.ui.tooling.preview.Preview |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel |
||||||
|
import com.gyf.csams.R |
||||||
|
import com.gyf.csams.main.model.MainViewModel |
||||||
|
import com.gyf.lib.uikit.Body |
||||||
|
import com.gyf.lib.uikit.MainColumnFrame |
||||||
|
import com.gyf.lib.uikit.Profile |
||||||
|
import com.gyf.lib.uikit.ShowSnackbar |
||||||
|
import com.gyf.lib.uikit.theme.CSAMSTheme |
||||||
|
|
||||||
|
class MainActivity : ComponentActivity() { |
||||||
|
override fun onCreate(savedInstanceState: Bundle?) { |
||||||
|
super.onCreate(savedInstanceState) |
||||||
|
setContent { |
||||||
|
Body { scaffoldState -> |
||||||
|
val model: MainViewModel = viewModel() |
||||||
|
val person by model.person.observeAsState() |
||||||
|
MainColumnFrame(background = { /*TODO*/ }) { |
||||||
|
person?.let { |
||||||
|
Profile( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.3F) |
||||||
|
.padding(10.dp), personInfoVo = it |
||||||
|
) |
||||||
|
} |
||||||
|
val context = LocalContext.current |
||||||
|
Column( |
||||||
|
modifier = Modifier.weight(0.7F), |
||||||
|
verticalArrangement = Arrangement.SpaceEvenly |
||||||
|
) { |
||||||
|
OutlinedButton(onClick = { |
||||||
|
context.startActivity(Intent(context, DepartmentActivity::class.java)) |
||||||
|
}, modifier = Modifier.fillMaxWidth()) { |
||||||
|
Text(text = context.getString(R.string.department_management)) |
||||||
|
} |
||||||
|
OutlinedButton(onClick = { /*TODO*/ }, modifier = Modifier.fillMaxWidth()) { |
||||||
|
Text(text = context.getString(R.string.association_management)) |
||||||
|
} |
||||||
|
OutlinedButton(onClick = { /*TODO*/ }, modifier = Modifier.fillMaxWidth()) { |
||||||
|
Text(text = context.getString(R.string.activity_management)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
ShowSnackbar(scaffoldState = scaffoldState) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
fun Greeting(name: String) { |
||||||
|
Text(text = "Hello $name!") |
||||||
|
} |
||||||
|
|
||||||
|
@Preview(showBackground = true) |
||||||
|
@Composable |
||||||
|
fun DefaultPreview() { |
||||||
|
CSAMSTheme { |
||||||
|
Greeting("Android") |
||||||
|
} |
||||||
|
} |
@ -1,16 +0,0 @@ |
|||||||
<resources xmlns:tools="http://schemas.android.com/tools"> |
|
||||||
<!-- Base application theme. --> |
|
||||||
<style name="Theme.CSAMS" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> |
|
||||||
<!-- Primary brand color. --> |
|
||||||
<item name="colorPrimary">@color/purple_200</item> |
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item> |
|
||||||
<item name="colorOnPrimary">@color/black</item> |
|
||||||
<!-- Secondary brand color. --> |
|
||||||
<item name="colorSecondary">@color/teal_200</item> |
|
||||||
<item name="colorSecondaryVariant">@color/teal_200</item> |
|
||||||
<item name="colorOnSecondary">@color/black</item> |
|
||||||
<!-- Status bar color. --> |
|
||||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item> |
|
||||||
<!-- Customize your theme here. --> |
|
||||||
</style> |
|
||||||
</resources> |
|
@ -0,0 +1,28 @@ |
|||||||
|
package com.gyf.csams.util |
||||||
|
|
||||||
|
import com.google.gson.Gson |
||||||
|
import com.google.gson.GsonBuilder |
||||||
|
import com.gyf.csams.association.model.Exam |
||||||
|
import com.gyf.lib.util.ApiResponse |
||||||
|
import com.gyf.lib.util.HttpCallback |
||||||
|
import java.lang.reflect.Type |
||||||
|
|
||||||
|
/** |
||||||
|
* http请求回调 |
||||||
|
* |
||||||
|
* @param T |
||||||
|
* @property action |
||||||
|
* @property onSuccess |
||||||
|
* @property onFail |
||||||
|
* @property type |
||||||
|
*/ |
||||||
|
class SimpleCallback<T>( |
||||||
|
private val action: String, |
||||||
|
private val onSuccess: (res: ApiResponse<T>) -> Unit, |
||||||
|
private val onFail: (error: String) -> Unit, |
||||||
|
private val type: Type |
||||||
|
) : HttpCallback<T>(action, onSuccess, onFail, type) { |
||||||
|
override val gson: Gson = GsonBuilder() |
||||||
|
.registerTypeAdapter(Exam::class.java, ExamDeserializer()) |
||||||
|
.create() |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package com.gyf.lib.uikit |
||||||
|
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions |
||||||
|
import androidx.compose.foundation.text.KeyboardOptions |
||||||
|
import androidx.compose.material.OutlinedTextField |
||||||
|
import androidx.compose.material.Text |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.runtime.livedata.observeAsState |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.platform.LocalFocusManager |
||||||
|
import androidx.compose.ui.text.input.ImeAction |
||||||
|
import androidx.compose.ui.text.input.VisualTransformation |
||||||
|
import androidx.lifecycle.LiveData |
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
import com.orhanobut.logger.Logger |
||||||
|
|
||||||
|
|
||||||
|
interface FormLength { |
||||||
|
val nameLengthError: String |
||||||
|
} |
||||||
|
|
||||||
|
abstract class FormName<T>(val formDesc: String) { |
||||||
|
protected val _formValue = MutableLiveData<T>() |
||||||
|
val formValue: LiveData<T> = _formValue |
||||||
|
val formPlaceholder = "请输入$formDesc" |
||||||
|
|
||||||
|
abstract fun onChange(value: T) |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 文本输入框控制 |
||||||
|
* |
||||||
|
* @property textLength |
||||||
|
* @constructor |
||||||
|
* |
||||||
|
* @param formDesc |
||||||
|
*/ |
||||||
|
open class StringForm(formDesc: String, val textLength: Int) : |
||||||
|
FormName<String>(formDesc = formDesc), |
||||||
|
FormLength { |
||||||
|
|
||||||
|
constructor(formDesc: String, textLength: Int, value: String) : this( |
||||||
|
formDesc = formDesc, |
||||||
|
textLength = textLength |
||||||
|
) { |
||||||
|
_formValue.value = value |
||||||
|
} |
||||||
|
|
||||||
|
override val nameLengthError = "${formDesc}不能超过最大长度$textLength" |
||||||
|
|
||||||
|
override fun onChange(value: String) { |
||||||
|
if (value.length > textLength) { |
||||||
|
_formValue.value = value.slice(IntRange(0, textLength - 1)) |
||||||
|
} else { |
||||||
|
_formValue.value = value |
||||||
|
} |
||||||
|
Logger.i("${formDesc}更新值:${_formValue.value}") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 通用文本输入框 |
||||||
|
* |
||||||
|
* @param T |
||||||
|
* @param modifier |
||||||
|
* @param form |
||||||
|
* @param singeLine |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun <T : StringForm> BaseTextField( |
||||||
|
modifier: Modifier = Modifier, |
||||||
|
form: T, |
||||||
|
singeLine: Boolean = false, |
||||||
|
keyboardOptions: KeyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done), |
||||||
|
isError: Boolean = false, |
||||||
|
visualTransformation: VisualTransformation = VisualTransformation.None |
||||||
|
) { |
||||||
|
val name: String by form.formValue.observeAsState("") |
||||||
|
val focusManager = LocalFocusManager.current |
||||||
|
OutlinedTextField( |
||||||
|
modifier = modifier, |
||||||
|
value = name, |
||||||
|
onValueChange = { form.onChange(it) }, |
||||||
|
label = { Text(text = form.formDesc) }, |
||||||
|
placeholder = { Text(text = form.formPlaceholder) }, |
||||||
|
singleLine = singeLine, |
||||||
|
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }), |
||||||
|
keyboardOptions = keyboardOptions, |
||||||
|
trailingIcon = { Text(text = "${name.length}/${form.textLength}") }, |
||||||
|
isError = isError, |
||||||
|
visualTransformation = visualTransformation |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,179 @@ |
|||||||
|
package com.gyf.lib.uikit |
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.* |
||||||
|
import androidx.compose.material.* |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.ui.Alignment |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.res.painterResource |
||||||
|
import androidx.navigation.NavHostController |
||||||
|
import androidx.navigation.compose.navigate |
||||||
|
import androidx.navigation.compose.rememberNavController |
||||||
|
import com.gyf.lib.uikit.theme.CSAMSTheme |
||||||
|
|
||||||
|
|
||||||
|
interface MenuEnum { |
||||||
|
val name: String |
||||||
|
} |
||||||
|
|
||||||
|
interface BottomBarMenu : MenuEnum { |
||||||
|
val selectedIcon: Int |
||||||
|
val unSelectedIcon: Int |
||||||
|
fun allMenu(): Array<out BottomBarMenu> |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 导航界面框架 |
||||||
|
* |
||||||
|
* @param background 背景 |
||||||
|
* @param mainMenu 菜单 |
||||||
|
* @param nav 导航 |
||||||
|
* @param body 内容 |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun <T : BottomBarMenu> MainColumnFrame( |
||||||
|
background: @Composable () -> Unit, |
||||||
|
mainMenu: T, |
||||||
|
nav: NavHostController, |
||||||
|
body: @Composable ColumnScope.() -> Unit |
||||||
|
) { |
||||||
|
Box(modifier = Modifier.fillMaxSize()) { |
||||||
|
background() |
||||||
|
Column { |
||||||
|
Column(modifier = Modifier.weight(0.9F), content = body) |
||||||
|
MainBottomAppBar( |
||||||
|
menu = mainMenu, |
||||||
|
nav = nav |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 界面框架 |
||||||
|
* |
||||||
|
* @param background |
||||||
|
* @param body |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun MainColumnFrame( |
||||||
|
background: @Composable () -> Unit, |
||||||
|
body: @Composable ColumnScope.() -> Unit |
||||||
|
) { |
||||||
|
Box(modifier = Modifier.fillMaxSize()) { |
||||||
|
background() |
||||||
|
Column(content = body) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 界面框架 |
||||||
|
* |
||||||
|
* @param background |
||||||
|
* @param body |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun MainBoxFrame( |
||||||
|
contentAlignment: Alignment = Alignment.TopStart, |
||||||
|
background: @Composable () -> Unit, |
||||||
|
body: @Composable BoxScope.() -> Unit |
||||||
|
) { |
||||||
|
Box( |
||||||
|
modifier = Modifier.fillMaxSize(), |
||||||
|
contentAlignment = contentAlignment |
||||||
|
) { |
||||||
|
background() |
||||||
|
body() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 底部菜单按钮 |
||||||
|
* |
||||||
|
* @param _menu |
||||||
|
* @param menu |
||||||
|
* @param modifier |
||||||
|
* @param onClick |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun <T : BottomBarMenu> MenuIconButton( |
||||||
|
_menu: T, |
||||||
|
menu: T, |
||||||
|
modifier: Modifier = Modifier, |
||||||
|
onClick: () -> Unit |
||||||
|
) { |
||||||
|
Row( |
||||||
|
modifier = modifier, horizontalArrangement = Arrangement.Center |
||||||
|
) { |
||||||
|
IconButton(onClick = onClick) { |
||||||
|
Icon( |
||||||
|
painter = painterResource(id = if (_menu == menu) menu.selectedIcon else menu.unSelectedIcon), |
||||||
|
contentDescription = null |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 主界面底部菜单 |
||||||
|
* |
||||||
|
* @param menu |
||||||
|
* @param nav |
||||||
|
* @param modifier |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun <T : BottomBarMenu> MainBottomAppBar( |
||||||
|
menu: T, |
||||||
|
nav: NavHostController, |
||||||
|
modifier: Modifier = Modifier |
||||||
|
) { |
||||||
|
BottomAppBar(backgroundColor = MaterialTheme.colors.background, modifier = modifier) { |
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { |
||||||
|
menu.allMenu().forEach { |
||||||
|
MenuIconButton(_menu = menu, menu = it, |
||||||
|
onClick = { nav.navigate(it.name) }) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
* |
||||||
|
* @param content |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun Body(content: @Composable (scaffoldState: ScaffoldState) -> Unit) { |
||||||
|
CSAMSTheme { |
||||||
|
Surface(color = MaterialTheme.colors.background) { |
||||||
|
val scaffoldState = rememberScaffoldState() |
||||||
|
|
||||||
|
Scaffold(scaffoldState = scaffoldState) { |
||||||
|
content(scaffoldState = scaffoldState) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 带导航的主体 |
||||||
|
* |
||||||
|
* @param content |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun Body(content: @Composable (nav: NavHostController, scaffoldState: ScaffoldState) -> Unit) { |
||||||
|
CSAMSTheme { |
||||||
|
Surface(color = MaterialTheme.colors.background) { |
||||||
|
val navController = rememberNavController() |
||||||
|
val scaffoldState = rememberScaffoldState() |
||||||
|
|
||||||
|
Scaffold(scaffoldState = scaffoldState) { |
||||||
|
content(nav = navController, scaffoldState = scaffoldState) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
package com.gyf.lib.uikit |
||||||
|
|
||||||
|
import androidx.compose.foundation.Image |
||||||
|
import androidx.compose.foundation.border |
||||||
|
import androidx.compose.foundation.layout.* |
||||||
|
import androidx.compose.material.Card |
||||||
|
import androidx.compose.material.MaterialTheme |
||||||
|
import androidx.compose.material.Text |
||||||
|
import androidx.compose.runtime.* |
||||||
|
import androidx.compose.ui.Alignment |
||||||
|
import androidx.compose.ui.Modifier |
||||||
|
import androidx.compose.ui.graphics.ImageBitmap |
||||||
|
import androidx.compose.ui.platform.LocalContext |
||||||
|
import androidx.compose.ui.unit.dp |
||||||
|
import com.gyf.lib.util.ImageUtil |
||||||
|
|
||||||
|
|
||||||
|
abstract class PersonInfoVo { |
||||||
|
abstract val name: String |
||||||
|
abstract val duty: String |
||||||
|
abstract val headImg: String |
||||||
|
abstract val desc: String |
||||||
|
} |
||||||
|
|
||||||
|
@Composable |
||||||
|
fun Profile( |
||||||
|
modifier: Modifier, |
||||||
|
personInfoVo: PersonInfoVo |
||||||
|
) { |
||||||
|
var headImg: ImageBitmap? by remember { |
||||||
|
mutableStateOf(null) |
||||||
|
} |
||||||
|
val context = LocalContext.current |
||||||
|
LaunchedEffect(personInfoVo.headImg) { |
||||||
|
headImg = ImageUtil.getImage(context = context, personInfoVo.headImg) |
||||||
|
} |
||||||
|
Column( |
||||||
|
modifier = modifier, |
||||||
|
) { |
||||||
|
Row( |
||||||
|
modifier = Modifier |
||||||
|
.fillMaxWidth() |
||||||
|
.weight(0.7F) |
||||||
|
.border(width = 1.dp, color = MaterialTheme.colors.background), |
||||||
|
horizontalArrangement = Arrangement.SpaceBetween |
||||||
|
) { |
||||||
|
headImg.let { |
||||||
|
if (it != null) { |
||||||
|
Image( |
||||||
|
bitmap = it, |
||||||
|
contentDescription = null, |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.4F) |
||||||
|
.fillMaxHeight() |
||||||
|
) |
||||||
|
} else { |
||||||
|
Box( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.4F) |
||||||
|
.fillMaxHeight(), |
||||||
|
contentAlignment = Alignment.Center |
||||||
|
) { |
||||||
|
Text(text = "头像加载失败") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Column( |
||||||
|
modifier = Modifier |
||||||
|
.weight(0.4F) |
||||||
|
.fillMaxHeight() |
||||||
|
.border(width = 1.dp, color = MaterialTheme.colors.background), |
||||||
|
verticalArrangement = Arrangement.SpaceEvenly, |
||||||
|
horizontalAlignment = Alignment.CenterHorizontally |
||||||
|
) { |
||||||
|
Text(text = personInfoVo.name) |
||||||
|
Text(text = personInfoVo.duty) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(0.05F)) |
||||||
|
|
||||||
|
Card( |
||||||
|
backgroundColor = MaterialTheme.colors.background, |
||||||
|
modifier = Modifier.weight(0.15F) |
||||||
|
) { |
||||||
|
Row( |
||||||
|
modifier = Modifier.fillMaxWidth(), |
||||||
|
horizontalArrangement = Arrangement.Center |
||||||
|
) { |
||||||
|
Text(text = personInfoVo.desc) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
package com.gyf.lib.uikit |
||||||
|
|
||||||
|
import androidx.compose.material.ScaffoldState |
||||||
|
import androidx.compose.material.SnackbarDuration |
||||||
|
import androidx.compose.material.SnackbarResult |
||||||
|
import androidx.compose.runtime.Composable |
||||||
|
import androidx.compose.runtime.LaunchedEffect |
||||||
|
import androidx.compose.runtime.getValue |
||||||
|
import androidx.compose.runtime.livedata.observeAsState |
||||||
|
import androidx.lifecycle.LiveData |
||||||
|
import androidx.lifecycle.MutableLiveData |
||||||
|
import androidx.lifecycle.ViewModel |
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel |
||||||
|
import com.orhanobut.logger.Logger |
||||||
|
import kotlinx.coroutines.launch |
||||||
|
|
||||||
|
|
||||||
|
data class SnackBar( |
||||||
|
val message: String?, |
||||||
|
val actionLabel: String? = null, |
||||||
|
val duration: SnackbarDuration = SnackbarDuration.Short, |
||||||
|
val callback: () -> Unit? |
||||||
|
) |
||||||
|
|
||||||
|
/** |
||||||
|
* snackbar |
||||||
|
* |
||||||
|
*/ |
||||||
|
class ScaffoldModel : ViewModel() { |
||||||
|
private val _data = MutableLiveData<SnackBar>() |
||||||
|
val data: LiveData<SnackBar> = _data |
||||||
|
|
||||||
|
fun update(message: String? = null, actionLabel: String? = null, callback: () -> Unit? = {}) { |
||||||
|
if (message == null) { |
||||||
|
_data.value = null |
||||||
|
} else { |
||||||
|
_data.value = |
||||||
|
SnackBar(message = message, actionLabel = actionLabel, callback = callback) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 底部提示 |
||||||
|
* |
||||||
|
* @param model |
||||||
|
* @param scaffoldState |
||||||
|
*/ |
||||||
|
@Composable |
||||||
|
fun ShowSnackbar(model: ScaffoldModel = viewModel(), scaffoldState: ScaffoldState) { |
||||||
|
val snackBar: SnackBar? by model.data.observeAsState() |
||||||
|
snackBar?.apply { |
||||||
|
if (message != null) { |
||||||
|
LaunchedEffect(scaffoldState) { |
||||||
|
launch { |
||||||
|
if (actionLabel != null) { |
||||||
|
val result = scaffoldState.snackbarHostState.showSnackbar( |
||||||
|
message = message, actionLabel = actionLabel, |
||||||
|
duration = duration |
||||||
|
|
||||||
|
) |
||||||
|
when (result) { |
||||||
|
SnackbarResult.ActionPerformed -> { |
||||||
|
Logger.i("点击操作按钮") |
||||||
|
callback() |
||||||
|
} |
||||||
|
SnackbarResult.Dismissed -> { |
||||||
|
Logger.d("窗口消失") |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
scaffoldState.snackbarHostState.showSnackbar(message = message) |
||||||
|
} |
||||||
|
model.update() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package com.gyf.csams.util |
package com.gyf.lib.util |
||||||
|
|
||||||
import android.content.Context |
import android.content.Context |
||||||
import androidx.compose.ui.graphics.ImageBitmap |
import androidx.compose.ui.graphics.ImageBitmap |
@ -1,4 +1,4 @@ |
|||||||
package com.gyf.csams.util |
package com.gyf.lib.util |
||||||
|
|
||||||
import okhttp3.internal.toHexString |
import okhttp3.internal.toHexString |
||||||
import java.text.SimpleDateFormat |
import java.text.SimpleDateFormat |
Loading…
Reference in new issue