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 androidx.compose.ui.graphics.ImageBitmap |
@ -1,4 +1,4 @@ |
||||
package com.gyf.csams.util |
||||
package com.gyf.lib.util |
||||
|
||||
import okhttp3.internal.toHexString |
||||
import java.text.SimpleDateFormat |
Loading…
Reference in new issue