从前台抽取compose函数到公共库

后台增加部门管理主界面
master
pan 4 years ago
parent 8e527cf491
commit 671b682711
  1. 2
      background/build.gradle.kts
  2. 12
      background/src/main/AndroidManifest.xml
  3. 38
      background/src/main/java/com/gyf/csams/MainActivity.kt
  4. 17
      background/src/main/java/com/gyf/csams/MainApplication.kt
  5. 11
      background/src/main/java/com/gyf/csams/account/model/LoginViewModel.kt
  6. 66
      background/src/main/java/com/gyf/csams/account/ui/LoginActivity.kt
  7. 4
      background/src/main/java/com/gyf/csams/main/model/DepartmentViewModel.kt
  8. 42
      background/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  9. 172
      background/src/main/java/com/gyf/csams/main/ui/DepartmentActivity.kt
  10. 79
      background/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  11. 14
      background/src/main/res/values-en/strings.xml
  12. 16
      background/src/main/res/values-night/themes.xml
  13. 14
      background/src/main/res/values-zh/strings.xml
  14. 13
      background/src/main/res/values/strings.xml
  15. 2
      foreground/src/main/AndroidManifest.xml
  16. 6
      foreground/src/main/java/com/gyf/csams/InitViewModel.kt
  17. 5
      foreground/src/main/java/com/gyf/csams/MainApplication.kt
  18. 8
      foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt
  19. 4
      foreground/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt
  20. 12
      foreground/src/main/java/com/gyf/csams/activity/model/ActivityDetailViewModel.kt
  21. 24
      foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt
  22. 2
      foreground/src/main/java/com/gyf/csams/association/model/AssociationViewModel.kt
  23. 2
      foreground/src/main/java/com/gyf/csams/association/model/ExamViewModel.kt
  24. 2
      foreground/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt
  25. 2
      foreground/src/main/java/com/gyf/csams/association/model/RenameViewModel.kt
  26. 9
      foreground/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt
  27. 11
      foreground/src/main/java/com/gyf/csams/association/ui/ExamActivity.kt
  28. 6
      foreground/src/main/java/com/gyf/csams/association/ui/ReNameActivity.kt
  29. 26
      foreground/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
  30. 17
      foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  31. 67
      foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  32. 6
      foreground/src/main/java/com/gyf/csams/message/model/SysMessageViewModel.kt
  33. 8
      foreground/src/main/java/com/gyf/csams/message/ui/MessageActivity.kt
  34. 10
      foreground/src/main/java/com/gyf/csams/message/ui/SysMessageActivity.kt
  35. 225
      foreground/src/main/java/com/gyf/csams/uikit/BaseView.kt
  36. 76
      foreground/src/main/java/com/gyf/csams/uikit/ViewModel.kt
  37. 2
      foreground/src/main/java/com/gyf/csams/util/GsonUtil.kt
  38. 28
      foreground/src/main/java/com/gyf/csams/util/HttpCallback.kt
  39. 3
      foreground/src/main/java/com/gyf/csams/util/TokenUtil.kt
  40. 8
      foreground/src/test/java/com/gyf/csams/ExampleUnitTest.kt
  41. 96
      lib/src/main/java/com/gyf/lib/uikit/BaseTextField.kt
  42. 179
      lib/src/main/java/com/gyf/lib/uikit/MainFrame.kt
  43. 94
      lib/src/main/java/com/gyf/lib/uikit/Profile.kt
  44. 81
      lib/src/main/java/com/gyf/lib/uikit/Snackbar.kt
  45. 32
      lib/src/main/java/com/gyf/lib/util/HttpUtil.kt
  46. 2
      lib/src/main/java/com/gyf/lib/util/ImageUtil.kt
  47. 2
      lib/src/main/java/com/gyf/lib/util/RandomUtil.kt

@ -25,6 +25,7 @@ android {
manifestPlaceholders.apply {
this["background_app_name"] = appName
}
buildConfigField(type = "String", name = "background_app_name", value = "\"$appName\"")
}
release {
isMinifyEnabled = false
@ -35,6 +36,7 @@ android {
manifestPlaceholders.apply {
this["background_app_name"] = appName
}
buildConfigField(type = "String", name = "background_app_name", value = "\"$appName\"")
}
}
compileOptions {

@ -9,9 +9,10 @@
android:label="${background_app_name}"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.CSAMS">
android:theme="@style/Theme.CSAMS"
android:name=".MainApplication">
<activity
android:name=".MainActivity"
android:name=".account.ui.LoginActivity"
android:exported="true"
android:label="${background_app_name}"
android:theme="@style/Theme.CSAMS.NoActionBar">
@ -21,6 +22,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".main.ui.MainActivity"
android:exported="true" />
<activity
android:name=".main.ui.DepartmentActivity"
android:exported="true" />
</application>
</manifest>

@ -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")
}
}

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="department_management">部门管理</string>
<string name="association_management">社团管理</string>
<string name="activity_management">活动管理</string>
<string name="secretariat">秘书部</string>
<string name="propaganda_department">宣传部</string>
<string name="public_relations_department">外联部</string>
<string name="close">关闭</string>
<string name="secretariat_desc">秘书部作为社团学生部和各社团之间连接的桥梁,地位非常重要,主要职责包括: 客服方面。不仅需要解答学生社团的各类问题,处理各类投诉,收发各类文件还要代表社团联向社团传递各类信息和要求。社团联秘书部每月需整理社团联各部门的绩效,在期末还需参与社团评分工作。 管理协会的人事档案和活动档案。联系成员举行会议,并做好会议记录。活动期间人员协调调用。将协会的最新信息传递给会员,将会员的意见和建议及时反馈至例会委员会。全权处理社团财务管理工作。</string>
<string name="propaganda_desc">专门负责学校社团各种活动的宣传,出海报以及网络上的宣传工作。 负责学生思想政治教育和学生会各项宣传报道工作的组织和规划。 围绕学院各阶段的中心工作和学院组织和各项活动,做好宣传鼓动工作。 组织学生参加社团开设的学术讲座和学术活动,督促和指导各班学习委员开展工作。</string>
<string name="public_relations_department_desc"><![CDATA[外联部最主要的任务就是为社团和学生活动筹集资金,也就是拉赞助。 为社团部门的各项活动开展做好外联工作。 筹措社团部门各项活动的资金。 负责与兄弟院校的交流联络工作 。 负责社团部门各项活动的节目演出、协助各文体性质社团开展各项活动。 经常性参与开展品牌文体活动。]]></string>
<string name="department_desc">部门介绍</string>
</resources>

@ -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,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="department_management">部门管理</string>
<string name="association_management">社团管理</string>
<string name="activity_management">活动管理</string>
<string name="secretariat">秘书部</string>
<string name="propaganda_department">宣传部</string>
<string name="public_relations_department">外联部</string>
<string name="close">关闭</string>
<string name="secretariat_desc">秘书部作为社团学生部和各社团之间连接的桥梁,地位非常重要,主要职责包括: 客服方面。不仅需要解答学生社团的各类问题,处理各类投诉,收发各类文件还要代表社团联向社团传递各类信息和要求。社团联秘书部每月需整理社团联各部门的绩效,在期末还需参与社团评分工作。 管理协会的人事档案和活动档案。联系成员举行会议,并做好会议记录。活动期间人员协调调用。将协会的最新信息传递给会员,将会员的意见和建议及时反馈至例会委员会。全权处理社团财务管理工作。</string>
<string name="propaganda_desc">专门负责学校社团各种活动的宣传,出海报以及网络上的宣传工作。 负责学生思想政治教育和学生会各项宣传报道工作的组织和规划。 围绕学院各阶段的中心工作和学院组织和各项活动,做好宣传鼓动工作。 组织学生参加社团开设的学术讲座和学术活动,督促和指导各班学习委员开展工作。</string>
<string name="public_relations_department_desc"><![CDATA[外联部最主要的任务就是为社团和学生活动筹集资金,也就是拉赞助。 为社团部门的各项活动开展做好外联工作。 筹措社团部门各项活动的资金。 负责与兄弟院校的交流联络工作 。 负责社团部门各项活动的节目演出、协助各文体性质社团开展各项活动。 经常性参与开展品牌文体活动。]]></string>
<string name="department_desc">部门介绍</string>
</resources>

@ -1,3 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="department_management">部门管理</string>
<string name="association_management">社团管理</string>
<string name="activity_management">活动管理</string>
<string name="secretariat">秘书部</string>
<string name="propaganda_department">宣传部</string>
<string name="public_relations_department">外联部</string>
<string name="close">关闭</string>
<string name="secretariat_desc">秘书部作为社团学生部和各社团之间连接的桥梁,地位非常重要,主要职责包括: 客服方面。不仅需要解答学生社团的各类问题,处理各类投诉,收发各类文件还要代表社团联向社团传递各类信息和要求。社团联秘书部每月需整理社团联各部门的绩效,在期末还需参与社团评分工作。 管理协会的人事档案和活动档案。联系成员举行会议,并做好会议记录。活动期间人员协调调用。将协会的最新信息传递给会员,将会员的意见和建议及时反馈至例会委员会。全权处理社团财务管理工作。</string>
<string name="propaganda_desc">专门负责学校社团各种活动的宣传,出海报以及网络上的宣传工作。 负责学生思想政治教育和学生会各项宣传报道工作的组织和规划。 围绕学院各阶段的中心工作和学院组织和各项活动,做好宣传鼓动工作。 组织学生参加社团开设的学术讲座和学术活动,督促和指导各班学习委员开展工作。</string>
<string name="public_relations_department_desc"><![CDATA[外联部最主要的任务就是为社团和学生活动筹集资金,也就是拉赞助。 为社团部门的各项活动开展做好外联工作。 筹措社团部门各项活动的资金。 负责与兄弟院校的交流联络工作 。 负责社团部门各项活动的节目演出、协助各文体性质社团开展各项活动。 经常性参与开展品牌文体活动。]]></string>
<string name="department_desc">部门介绍</string>
</resources>

@ -16,7 +16,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.CSAMS"
android:name=".APP">
android:name=".MainApplication">
<!--初始化界面-->
<activity

@ -7,7 +7,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.gyf.csams.util.*
import com.gyf.csams.util.AppDatabase
import com.gyf.csams.util.SimpleCallback
import com.gyf.csams.util.Token
import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.HttpClient
import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch

@ -10,13 +10,14 @@ import coil.memory.MemoryCache
import coil.request.ImageRequest
import coil.util.CoilUtils
import com.gyf.csams.uikit.BackgroundImage
import com.gyf.csams.util.ImageUtil
import com.gyf.lib.util.ImageUtil
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.DiskLogAdapter
import com.orhanobut.logger.Logger
import okhttp3.OkHttpClient
class APP : Application(), ImageLoaderFactory {
class MainApplication : Application(), ImageLoaderFactory {
private val backgroundImage = mutableMapOf<BackgroundImage, MemoryCache.Key?>()

@ -13,8 +13,12 @@ import com.gyf.csams.AccountApi
import com.gyf.csams.Api
import com.gyf.csams.InitActivity
import com.gyf.csams.account.ui.AccountRoute
import com.gyf.csams.uikit.StringForm
import com.gyf.csams.util.*
import com.gyf.csams.util.AppDatabase
import com.gyf.csams.util.SimpleCallback
import com.gyf.csams.util.TokenResDto
import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.HttpClient
import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

@ -28,8 +28,8 @@ import com.gyf.csams.BuildConfig
import com.gyf.csams.account.model.AccountViewModel
import com.gyf.csams.account.model.DialogMessage
import com.gyf.csams.uikit.AnimationText
import com.gyf.csams.uikit.BaseTextField
import com.gyf.csams.uikit.Body
import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.Body
import com.orhanobut.logger.Logger

@ -5,10 +5,14 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.*
import com.gyf.csams.util.randomChinese
import com.gyf.csams.util.randomDateTime
import com.gyf.csams.util.randomNum
import com.gyf.csams.uikit.ActivityDetailMenu
import com.gyf.csams.uikit.ScrollList
import com.gyf.csams.uikit.SendInterface
import com.gyf.csams.uikit.TopMenuInterface
import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomDateTime
import com.gyf.lib.util.randomNum
import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
import java.util.*

@ -23,7 +23,11 @@ import androidx.navigation.compose.composable
import com.gyf.csams.R
import com.gyf.csams.activity.model.*
import com.gyf.csams.uikit.*
import com.gyf.csams.util.format
import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.uikit.ShowSnackbar
import com.gyf.lib.util.format
import com.orhanobut.logger.Logger
/**
@ -104,7 +108,12 @@ class ActivityDetailActivity : ComponentActivity() {
*/
@Composable
private fun Info() {
MainFrame(background = { Background(image = BackgroundImage.ActivityInfo, alpha = 0.7F) }) {
MainColumnFrame(background = {
Background(
image = BackgroundImage.ActivityInfo,
alpha = 0.7F
)
}) {
RectList(modifier = Modifier.weight(0.4F))
ActivityDesc(modifier = Modifier.weight(0.4F))
Spacer(modifier = Modifier.weight(0.05F))
@ -187,7 +196,7 @@ class ActivityDetailActivity : ComponentActivity() {
model: ActivityPhotoViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel()
) {
MainFrame(background = {
MainColumnFrame(background = {
Background(
image = BackgroundImage.ActivityPhoto,
alpha = 0.7F
@ -271,7 +280,7 @@ class ActivityDetailActivity : ComponentActivity() {
model: ActivityMemberViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel()
) {
MainFrame(background = {
MainColumnFrame(background = {
Background(
image = BackgroundImage.ActivityMember,
alpha = 0.7F
@ -362,7 +371,12 @@ class ActivityDetailActivity : ComponentActivity() {
*/
@Composable
private fun BBS(model: BBSViewModel = viewModel(), scaffoldModel: ScaffoldModel = viewModel()) {
MainFrame(background = { Background(image = BackgroundImage.ActivityBBS, alpha = 0.7F) }) {
MainColumnFrame(background = {
Background(
image = BackgroundImage.ActivityBBS,
alpha = 0.7F
)
}) {
Row(
modifier = Modifier

@ -7,8 +7,8 @@ import androidx.lifecycle.viewModelScope
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.AssociationMenu
import com.gyf.csams.uikit.ScrollList
import com.gyf.csams.uikit.StringForm
import com.gyf.csams.uikit.TopMenuInterface
import com.gyf.lib.uikit.StringForm
import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch

@ -5,7 +5,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.ScrollList
import com.gyf.csams.uikit.StringForm
import com.gyf.lib.uikit.StringForm
import kotlinx.coroutines.launch
import kotlin.random.Random

@ -5,7 +5,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.StringForm
import com.gyf.lib.uikit.StringForm
data class Image(val uri: Uri, val createTime: Long, val size: Long)

@ -2,7 +2,7 @@ package com.gyf.csams.association.model
import androidx.lifecycle.ViewModel
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.StringForm
import com.gyf.lib.uikit.StringForm
/**
* 社团重命名状态管理

@ -29,7 +29,8 @@ import com.gyf.csams.R
import com.gyf.csams.activity.ui.ActivityDetailActivity
import com.gyf.csams.association.model.*
import com.gyf.csams.uikit.*
import com.gyf.csams.util.randomChinese
import com.gyf.lib.uikit.*
import com.gyf.lib.util.randomChinese
import com.orhanobut.logger.Logger
@ -179,7 +180,7 @@ class AssociationActivity : ComponentActivity() {
*/
@Composable
private fun Member() {
MainFrame(background = { Background(image = BackgroundImage.AssociationMain) }) {
MainColumnFrame(background = { Background(image = BackgroundImage.AssociationMain) }) {
val searchWeight = 0.2F
Search(
modifier = Modifier
@ -294,7 +295,7 @@ class AssociationActivity : ComponentActivity() {
*/
@Composable
private fun Main() {
MainFrame(background = {
MainColumnFrame(background = {
Background(image = BackgroundImage.AssociationMain, alpha = 0.7F)
}) {
val nameW = 0.1F
@ -391,7 +392,7 @@ class AssociationActivity : ComponentActivity() {
*/
@Composable
private fun AssociationList() {
MainFrame(background = {
MainColumnFrame(background = {
Background(
image = BackgroundImage.AssociationMain,
alpha = 07F

@ -20,7 +20,9 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R
import com.gyf.csams.association.model.*
import com.gyf.csams.uikit.*
import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage
import com.gyf.lib.uikit.*
/**
@ -38,7 +40,12 @@ class ExamActivity : ComponentActivity() {
setContent {
Body { scaffoldState ->
MainFrame(background = { Background(image = BackgroundImage.Exam, alpha = 0.6F) }) {
MainColumnFrame(background = {
Background(
image = BackgroundImage.Exam,
alpha = 0.6F
)
}) {
Spacer(modifier = Modifier.weight(0.1F))
Title(modifier = Modifier.weight(0.1F))
Exam(modifier = Modifier.weight(0.8F))

@ -13,7 +13,9 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.association.model.RenameViewModel
import com.gyf.csams.uikit.*
import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage
import com.gyf.lib.uikit.*
/**
* 社团重命名
@ -25,7 +27,7 @@ class ReNameActivity : ComponentActivity() {
setContent {
Body { scaffoldState ->
MainFrame(background = { Background(image = BackgroundImage.Rename) }) {
MainColumnFrame(background = { Background(image = BackgroundImage.Rename) }) {
Spacer(
modifier = Modifier
.weight(0.2F)

@ -34,7 +34,9 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R
import com.gyf.csams.association.model.RegAssociationViewModel
import com.gyf.csams.uikit.*
import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage
import com.gyf.lib.uikit.*
import com.orhanobut.logger.Logger
@ -48,17 +50,17 @@ class RegAssociationActivity : ComponentActivity() {
setContent {
Body { scaffoldState ->
MainFrame(background = {
Background(
BackgroundImage.RegAssociation,
alpha = 0.5F
)
}) {
Spacer(
modifier = Modifier
.weight(0.1F)
)
Title()
MainColumnFrame(background = {
Background(
BackgroundImage.RegAssociation,
alpha = 0.5F
)
}) {
Spacer(
modifier = Modifier
.weight(0.1F)
)
Title()
Name()
Desc(
modifier = Modifier

@ -7,9 +7,10 @@ import androidx.lifecycle.viewModelScope
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.ScrollList
import com.gyf.csams.uikit.SendInterface
import com.gyf.csams.uikit.StringForm
import com.gyf.csams.util.randomChinese
import com.gyf.csams.util.randomNum
import com.gyf.lib.uikit.PersonInfoVo
import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomNum
import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@ -166,11 +167,11 @@ class ListViewModel : ScrollList<AssociationDto>() {
*/
data class InfoVo(
val studentId: String,
val name: String,
val duty: String,
val headImg: String,
val desc: String
)
override val name: String,
override val duty: String,
override val headImg: String,
override val desc: String
) : PersonInfoVo()
/**
* 个人中心

@ -33,7 +33,8 @@ import com.gyf.csams.association.ui.RegAssociationActivity
import com.gyf.csams.main.model.*
import com.gyf.csams.message.ui.MessageActivity
import com.gyf.csams.uikit.*
import com.gyf.csams.util.randomChinese
import com.gyf.lib.uikit.*
import com.gyf.lib.util.randomChinese
/**
@ -94,7 +95,7 @@ class MainActivity : ComponentActivity() {
model: CenterViewModel = viewModel(),
navController: NavHostController
) {
MainFrame(
MainColumnFrame(
background = { Background(image = BackgroundImage.Center, alpha = 0.5F) },
mainMenu = MainMenu.Center,
nav = navController
@ -103,58 +104,16 @@ class MainActivity : ComponentActivity() {
val info by model.info.observeAsState()
Column(
modifier = Modifier
.weight(0.3F)
.padding(10.dp),
) {
info?.let {
Row(
modifier = Modifier
.fillMaxWidth()
.weight(0.7F)
.border(width = 1.dp, color = MaterialTheme.colors.background),
horizontalArrangement = Arrangement.SpaceBetween
) {
Image(
painter = painterResource(
id = R.drawable.ic_launcher_foreground
),
contentDescription = null,
modifier = Modifier
.weight(0.4F)
.fillMaxHeight()
)
Column(
modifier = Modifier
.weight(0.4F)
.fillMaxHeight()
.border(width = 1.dp, color = MaterialTheme.colors.background),
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = it.name)
Text(text = it.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 = it.desc)
}
}
}
info?.let {
Profile(
modifier = Modifier
.weight(0.3F)
.padding(10.dp),
personInfoVo = it
)
}
Column(
modifier = Modifier.weight(0.7F),
verticalArrangement = Arrangement.SpaceEvenly
@ -201,7 +160,7 @@ class MainActivity : ComponentActivity() {
*/
@Composable
private fun Main(navController: NavHostController) {
MainFrame(
MainColumnFrame(
background = { Background(image = BackgroundImage.Main) },
mainMenu = MainMenu.Main,
nav = navController
@ -232,7 +191,7 @@ class MainActivity : ComponentActivity() {
*/
@Composable
private fun AssociationList(navController: NavHostController) {
MainFrame(
MainColumnFrame(
background = { Background(image = BackgroundImage.AssociationList) },
mainMenu = MainMenu.List,
nav = navController

@ -3,9 +3,9 @@ package com.gyf.csams.message.model
import androidx.lifecycle.viewModelScope
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.uikit.ScrollList
import com.gyf.csams.util.randomChinese
import com.gyf.csams.util.randomDateTime
import com.gyf.csams.util.randomNum
import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomDateTime
import com.gyf.lib.util.randomNum
import kotlinx.coroutines.launch
import java.util.*
import kotlin.random.Random

@ -19,7 +19,11 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R
import com.gyf.csams.message.model.MessageType
import com.gyf.csams.message.model.MessageViewModel
import com.gyf.csams.uikit.*
import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage
import com.gyf.csams.uikit.TextTopAppBar
import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame
/**
* 消息界面
@ -33,7 +37,7 @@ class MessageActivity : ComponentActivity() {
Body { _ ->
val model: MessageViewModel = viewModel()
val context = LocalContext.current
MainFrame(background = {
MainColumnFrame(background = {
Background(
image = BackgroundImage.ActivityMessage,
alpha = 0.6F

@ -19,8 +19,12 @@ import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.message.model.*
import com.gyf.csams.uikit.*
import com.gyf.csams.util.format
import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage
import com.gyf.csams.uikit.TextTopAppBar
import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.util.format
/**
* 系统通知
@ -32,7 +36,7 @@ class SysMessageActivity : ComponentActivity() {
setContent {
Body { _ ->
MainFrame(background = {
MainColumnFrame(background = {
Background(
image = BackgroundImage.ActivityMessage,
alpha = 0.6F

@ -9,8 +9,6 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
@ -21,10 +19,7 @@ import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@ -33,13 +28,11 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.navigate
import androidx.navigation.compose.rememberNavController
import com.gyf.csams.APP
import com.gyf.csams.MainApplication
import com.gyf.csams.R
import com.gyf.csams.main.model.MarqueeViewModel
import com.gyf.lib.uikit.*
import com.gyf.lib.uikit.theme.CSAMSTheme
import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
/**
* 淡入淡出并且颜色变化文本
@ -70,9 +63,9 @@ fun AnimationText(text: String) {
*
*/
enum class MainMenu(
@DrawableRes val selectedIcon: Int,
@DrawableRes val unSelectedIcon: Int
) {
@DrawableRes override val selectedIcon: Int,
@DrawableRes override val unSelectedIcon: Int
) : BottomBarMenu {
//主页
Main(R.drawable.ic_home_fill, R.drawable.ic_home),
@ -80,56 +73,12 @@ enum class MainMenu(
List(R.drawable.ic_all_fill, R.drawable.ic_all),
//个人中心
Center(R.drawable.ic_account_fill, R.drawable.ic_account)
}
Center(R.drawable.ic_account_fill, R.drawable.ic_account);
/**
* 底部菜单按钮
*
* @param _menu
* @param menu
* @param modifier
* @param onClick
*/
@Composable
fun MenuIconButton(
_menu: MainMenu,
menu: MainMenu,
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 MainBottomAppBar(menu: MainMenu, nav: NavHostController, modifier: Modifier = Modifier) {
BottomAppBar(backgroundColor = MaterialTheme.colors.background, modifier = modifier) {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
MenuIconButton(_menu = menu, menu = MainMenu.Main,
onClick = { nav.navigate(MainMenu.Main.name) })
MenuIconButton(_menu = menu, menu = MainMenu.List,
onClick = { nav.navigate(MainMenu.List.name) })
MenuIconButton(_menu = menu, menu = MainMenu.Center,
onClick = { nav.navigate(MainMenu.Center.name) })
}
override fun allMenu(): Array<out BottomBarMenu> {
return values()
}
}
@ -154,10 +103,9 @@ interface TopMenuInterface<T : TopBarMenu> {
}
}
interface TopBarMenu {
val menuName: String
val name: String
interface TopBarMenu : MenuEnum {
val menuName: String
}
@ -417,46 +365,6 @@ fun MarqueeText(model: MarqueeViewModel = viewModel(), offset: State<Float>) {
}
/**
* 导航界面框架
*
* @param background 背景
* @param mainMenu 菜单
* @param nav 导航
* @param body 内容
*/
@Composable
fun MainFrame(
background: @Composable () -> Unit,
mainMenu: MainMenu,
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 MainFrame(background: @Composable () -> Unit, body: @Composable ColumnScope.() -> Unit) {
Box(modifier = Modifier.fillMaxSize()) {
background()
Column(content = body)
}
}
/**
* 图片轮播
@ -475,80 +383,6 @@ fun Carousel(
}
}
/**
* 通用文本输入框
*
* @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
)
}
/**
* 底部提示
*
* @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()
}
}
}
}
}
/**
* 界面背景
*
@ -602,7 +436,7 @@ enum class BackgroundImage(@DrawableRes val id: Int) {
*/
@Composable
fun Background(image: BackgroundImage, alpha: Float = DefaultAlpha) {
val app = LocalContext.current.applicationContext as APP
val app = LocalContext.current.applicationContext as MainApplication
var i: ImageBitmap? by remember {
mutableStateOf(null)
}
@ -621,43 +455,6 @@ fun Background(image: BackgroundImage, alpha: Float = DefaultAlpha) {
}
/**
*
*
* @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)
}
}
}
}
/**
* 活动海报
*

@ -1,90 +1,18 @@
package com.gyf.csams.uikit
import android.app.Application
import androidx.compose.material.SnackbarDuration
import androidx.compose.ui.graphics.ImageBitmap
import androidx.lifecycle.*
import com.google.gson.reflect.TypeToken
import com.gyf.csams.R
import com.gyf.csams.util.HttpClient
import com.gyf.csams.util.ImageUtil
import com.gyf.csams.util.SimpleCallback
import com.gyf.lib.util.HttpClient
import com.gyf.lib.util.ImageUtil
import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
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}")
}
}
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)
}
}
}
class ImageModel(application: Application, private val urlPath: String) :
AndroidViewModel(application) {

@ -3,7 +3,7 @@ package com.gyf.csams.util
import com.google.gson.*
import com.google.gson.reflect.TypeToken
import com.gyf.csams.association.model.*
import com.gyf.csams.uikit.StringForm
import com.gyf.lib.uikit.StringForm
import java.lang.reflect.Type
class OpenQuestionsVoSerializer : JsonSerializer<OpenQuestionsVo> {

@ -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()
}

@ -4,14 +4,12 @@ import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.room.*
import kotlinx.serialization.Serializable
/**
* 登陆令牌
*/
@Entity
@Serializable
data class Token(
@PrimaryKey val studentId: String,
@ColumnInfo val token: String,
@ -24,7 +22,6 @@ data class Token(
* @property isValid
* @property token
*/
@Serializable
data class TokenResDto(val isValid: Boolean, val token: Token?)
@Dao

@ -2,9 +2,9 @@ package com.gyf.csams
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.gyf.csams.util.ApiResponse
import com.gyf.csams.util.randomChinese
import com.gyf.lib.uikit.TestLib
import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.randomChinese
import org.junit.Assert.assertEquals
import org.junit.Test
@ -27,8 +27,8 @@ class ExampleUnitTest {
@Test
fun testGson(){
// val e="{\"code\":200,\"message\":\"学号可注册\",\"body\":false}"
val c=ApiResponse(code = 200,message = "aaa",body= null)
val d=Gson().toJson(c)
val c = ApiResponse(code = 200, message = "aaa", body = null)
val d = Gson().toJson(c)
println(d)
val e=Gson().fromJson<ApiResponse<Boolean>>(d,object : TypeToken<ApiResponse<Boolean>>() {}.type)
println(e.body)

@ -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,8 +1,6 @@
package com.gyf.csams.util
package com.gyf.lib.util
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.gyf.csams.association.model.Exam
import com.orhanobut.logger.Logger
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
@ -114,28 +112,17 @@ object HttpClient {
*/
data class ApiResponse<T>(val code: Int, val message: String, val body: T? = null)
/**
* http请求回调
*
* @param T
* @property action
* @property onSuccess
* @property onFail
* @property type
*/
class SimpleCallback<T>(
interface GsonBuilderInterface {
val gson: Gson
}
abstract class HttpCallback<T>(
private val action: String,
private val onSuccess: (res: ApiResponse<T>) -> Unit,
private val onFail: (error: String) -> Unit,
private val type: Type
) : Callback {
companion object {
val gson: Gson = GsonBuilder()
.registerTypeAdapter(Exam::class.java, ExamDeserializer())
.create()
}
) : Callback, GsonBuilderInterface {
override fun onFailure(call: Call, e: IOException) {
when (e) {
is SocketTimeoutException -> {
@ -166,4 +153,5 @@ class SimpleCallback<T>(
Logger.e("${action}失败,请求响应码:${response.code}")
}
}
}
}

@ -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…
Cancel
Save