增加accompanist compose ui工具库

社团注册资料审核,增加防输入法遮挡
完善个人名片逻辑
社团接口更新
底部按钮组件更新
Http工具类响应回调逻辑完善
token管理逻辑完善
增加Vo统一管理
社团注册逻辑完善
社团列表逻辑完善
master
pan 3 years ago
parent 61671b3f1b
commit a3e4308037
  1. 18
      background/src/main/AndroidManifest.xml
  2. 8
      background/src/main/java/com/gyf/csams/InitActivity.kt
  3. 5
      background/src/main/java/com/gyf/csams/account/model/LoginViewModel.kt
  4. 70
      background/src/main/java/com/gyf/csams/main/model/AssociationManagementViewModel.kt
  5. 81
      background/src/main/java/com/gyf/csams/main/model/AuditAssociationViewModel.kt
  6. 11
      background/src/main/java/com/gyf/csams/main/model/BackgroundViewModel.kt
  7. 7
      background/src/main/java/com/gyf/csams/main/model/CheckActViewModel.kt
  8. 20
      background/src/main/java/com/gyf/csams/main/model/CheckQualityReportViewModel.kt
  9. 42
      background/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  10. 96
      background/src/main/java/com/gyf/csams/main/model/ManagementOfficerModel.kt
  11. 7
      background/src/main/java/com/gyf/csams/main/model/ManagerActViewModel.kt
  12. 3
      background/src/main/java/com/gyf/csams/main/model/MenuViewModel.kt
  13. 20
      background/src/main/java/com/gyf/csams/main/model/RenameViewModel.kt
  14. 12
      background/src/main/java/com/gyf/csams/main/ui/AssociationManagementActivity.kt
  15. 310
      background/src/main/java/com/gyf/csams/main/ui/AuditAssociationActivity.kt
  16. 13
      background/src/main/java/com/gyf/csams/main/ui/CheckActActivity.kt
  17. 15
      background/src/main/java/com/gyf/csams/main/ui/CheckQualityReportActivity.kt
  18. 24
      background/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  19. 18
      background/src/main/java/com/gyf/csams/main/ui/ManagementOfficerActivity.kt
  20. 6
      background/src/main/java/com/gyf/csams/main/ui/ManagerActActivity.kt
  21. 80
      background/src/main/java/com/gyf/csams/main/ui/NotificationActivity.kt
  22. 11
      background/src/main/java/com/gyf/csams/main/ui/RenameActivity.kt
  23. 59
      background/src/main/java/com/gyf/csams/uikit/Table.kt
  24. 5
      background/src/main/res/values-en/strings.xml
  25. 5
      background/src/main/res/values-zh/strings.xml
  26. 5
      background/src/main/res/values/strings.xml
  27. 13
      background/src/test/java/com/gyf/csams/ExampleUnitTest.kt
  28. 1
      foreground/src/main/AndroidManifest.xml
  29. 8
      foreground/src/main/java/com/gyf/csams/InitActivity.kt
  30. 44
      foreground/src/main/java/com/gyf/csams/account/model/AccountViewModel.kt
  31. 4
      foreground/src/main/java/com/gyf/csams/account/ui/AccountActivity.kt
  32. 49
      foreground/src/main/java/com/gyf/csams/activity/model/ActivityDetailViewModel.kt
  33. 7
      foreground/src/main/java/com/gyf/csams/activity/ui/ActivityDetailActivity.kt
  34. 10
      foreground/src/main/java/com/gyf/csams/association/model/AssociationViewModel.kt
  35. 45
      foreground/src/main/java/com/gyf/csams/association/model/ExamViewModel.kt
  36. 141
      foreground/src/main/java/com/gyf/csams/association/model/RegAssociationViewModel.kt
  37. 4
      foreground/src/main/java/com/gyf/csams/association/ui/AssociationActivity.kt
  38. 7
      foreground/src/main/java/com/gyf/csams/association/ui/ExamActivity.kt
  39. 115
      foreground/src/main/java/com/gyf/csams/association/ui/RegAssociationActivity.kt
  40. 211
      foreground/src/main/java/com/gyf/csams/main/model/MainViewModel.kt
  41. 39
      foreground/src/main/java/com/gyf/csams/main/ui/MainActivity.kt
  42. 11
      foreground/src/main/java/com/gyf/csams/message/model/ForegroundViewModel.kt
  43. 9
      foreground/src/main/java/com/gyf/csams/message/ui/SysMessageActivity.kt
  44. 2
      foreground/src/main/java/com/gyf/csams/uikit/BaseView.kt
  45. 10
      foreground/src/main/java/com/gyf/csams/uikit/ViewModel.kt
  46. 8
      foreground/src/main/java/com/gyf/csams/util/GsonUtil.kt
  47. 6
      foreground/src/main/java/com/gyf/csams/util/HttpCallback.kt
  48. 1
      foreground/src/main/res/values-en/strings.xml
  49. 1
      foreground/src/main/res/values-zh/strings.xml
  50. 1
      foreground/src/main/res/values/strings.xml
  51. 8
      foreground/src/test/java/com/gyf/csams/ExampleUnitTest.kt
  52. 9
      lib/build.gradle.kts
  53. 53
      lib/src/main/java/com/gyf/lib/model/AbstractLoginViewModel.kt
  54. 7
      lib/src/main/java/com/gyf/lib/model/ApplyViewModel.kt
  55. 20
      lib/src/main/java/com/gyf/lib/model/InitViewModel.kt
  56. 7
      lib/src/main/java/com/gyf/lib/model/ScrollViewModel.kt
  57. 61
      lib/src/main/java/com/gyf/lib/model/SysMessageViewModel.kt
  58. 7
      lib/src/main/java/com/gyf/lib/service/MessageService.kt
  59. 10
      lib/src/main/java/com/gyf/lib/uikit/AbstractInitActivity.kt
  60. 22
      lib/src/main/java/com/gyf/lib/uikit/MainFrame.kt
  61. 22
      lib/src/main/java/com/gyf/lib/uikit/Profile.kt
  62. 9
      lib/src/main/java/com/gyf/lib/util/Api.kt
  63. 20
      lib/src/main/java/com/gyf/lib/util/BottomButton.kt
  64. 25
      lib/src/main/java/com/gyf/lib/util/HttpUtil.kt
  65. 40
      lib/src/main/java/com/gyf/lib/util/TokenUtil.kt
  66. 477
      lib/src/main/java/com/gyf/lib/util/Vo.kt
  67. 27
      lib/src/main/java/com/gyf/lib/util/vo.kt
  68. 12
      lib/src/main/res/values-en/strings.xml
  69. 12
      lib/src/main/res/values-zh/strings.xml
  70. 12
      lib/src/main/res/values/strings.xml

@ -4,6 +4,8 @@
<!--访问网络--> <!--访问网络-->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -26,15 +28,31 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".account.ui.LoginActivity" /> <activity android:name=".account.ui.LoginActivity" />
<!--个人中心-->
<activity android:name=".main.ui.MainActivity" /> <activity android:name=".main.ui.MainActivity" />
<!--部门管理-->
<activity android:name=".main.ui.DepartmentActivity" /> <activity android:name=".main.ui.DepartmentActivity" />
<!--干事管理-->
<activity android:name=".main.ui.ManagementOfficerActivity" /> <activity android:name=".main.ui.ManagementOfficerActivity" />
<!--社团管理-->
<activity android:name=".main.ui.AssociationManagementActivity" /> <activity android:name=".main.ui.AssociationManagementActivity" />
<!--菜单-->
<activity android:name=".main.ui.MenuActivity" /> <activity android:name=".main.ui.MenuActivity" />
<!--社团换名-->
<activity android:name=".main.ui.RenameActivity" /> <activity android:name=".main.ui.RenameActivity" />
<!--活动管理-->
<activity android:name=".main.ui.ManagerActActivity" /> <activity android:name=".main.ui.ManagerActActivity" />
<!--审核社团注册-->
<activity
android:name=".main.ui.AuditAssociationActivity"
android:windowSoftInputMode="adjustResize" />
<!--活动申请书-->
<activity android:name=".main.ui.CheckActActivity" /> <activity android:name=".main.ui.CheckActActivity" />
<!--活动质量汇报单-->
<activity android:name=".main.ui.CheckQualityReportActivity" /> <activity android:name=".main.ui.CheckQualityReportActivity" />
<!--通知界面-->
<activity android:name=".main.ui.NotificationActivity" />
<!--通知服务-->
<service android:name="com.gyf.lib.service.MessageService" /> <service android:name="com.gyf.lib.service.MessageService" />
</application> </application>

@ -1,14 +1,20 @@
package com.gyf.csams package com.gyf.csams
import android.app.Activity import android.app.Activity
import com.google.gson.reflect.TypeToken
import com.gyf.csams.account.ui.LoginActivity import com.gyf.csams.account.ui.LoginActivity
import com.gyf.csams.main.ui.MainActivity import com.gyf.csams.main.ui.MainActivity
import com.gyf.lib.uikit.AbstractInitActivity import com.gyf.lib.uikit.AbstractInitActivity
import com.gyf.lib.util.AccountApi import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.ManagerVo
import java.lang.reflect.Type
class InitActivity : AbstractInitActivity() { class InitActivity : AbstractInitActivity<ManagerVo>() {
override val main: Class<out Activity> = MainActivity::class.java override val main: Class<out Activity> = MainActivity::class.java
override val login: Class<out Activity> = LoginActivity::class.java override val login: Class<out Activity> = LoginActivity::class.java
override val api: AccountApi = AccountApi.BackgroundToken override val api: AccountApi = AccountApi.BackgroundToken
override val typeToken: Type = object : TypeToken<ApiResponse<ManagerVo>>() {}.type
} }

@ -9,8 +9,7 @@ import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.AccountApi import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.ClientType import com.gyf.lib.util.ClientType
import com.gyf.lib.util.ManagerLoginVo
data class ManagerVo(val account: String, val password: String, val device: String)
class LoginViewModel(application: Application) : AbstractLoginViewModel(application) { class LoginViewModel(application: Application) : AbstractLoginViewModel(application) {
override val id = ValidStringForm(formDesc = "管理帐号", textLength = 8) override val id = ValidStringForm(formDesc = "管理帐号", textLength = 8)
@ -25,7 +24,7 @@ class LoginViewModel(application: Application) : AbstractLoginViewModel(applicat
override fun loginParam(): Any { override fun loginParam(): Any {
val account = "${id.formValue.value}" val account = "${id.formValue.value}"
val password = "${password.formValue.value}" val password = "${password.formValue.value}"
return ManagerVo( return ManagerLoginVo(
account = account, account = account,
password = password, password = password,
device = "${Build.MANUFACTURER} ${Build.MODEL}" device = "${Build.MANUFACTURER} ${Build.MODEL}"

@ -1,49 +1,12 @@
package com.gyf.csams.main.model package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import androidx.lifecycle.viewModelScope
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.AssociationLevel
import kotlinx.coroutines.launch import com.gyf.lib.util.AssociationVo
/**
* 社团级别
*
*/
enum class AssociationLevel {
A,
B,
C,
D
}
/**
* 所属院系
*
*/
enum class AssociationFaculty(val desc: String, val range: IntRange) {
ForeignLanguageDept("外语系", 0..0),
CivilEngineeringDept("土木工程", 1..10),
SEM("经理管理学院", 11..20),
MechanicalEngineeringDept("机械工程", 21..30),
TransportationDept("交通运输", 31..40),
ArchitectureAndArts("建筑与艺术", 41..50),
ElectricalDept("电气", 51..60),
MaterialsDept("材料", 61..70),
MessageDept("信息", 71..80),
MathematicsDept("数理", 81..90),
GraduateStudent("研究生", 91..99)
}
/**
* 社团
*
* @property name 社团名称
*/
data class AssociationVo(
val associationId: Long, val name: String, val commander: String,
val faculty: AssociationFaculty, val level: AssociationLevel, val desc: String
)
/** /**
* 数据状态管理 * 数据状态管理
@ -59,22 +22,7 @@ class AssociationManagementViewModel(application: Application) : ScrollViewModel
} }
override fun load() { override fun load() {
viewModelScope.launch { TODO()
_data.value?.apply {
repeat(initSize) {
add(
AssociationVo(
associationId = (0..65535L).random(),
name = randomChinese(5),
commander = randomChinese(3),
faculty = AssociationFaculty.values().random(),
level = AssociationLevel.values().random(),
desc = randomChinese(20)
)
)
}
}
}
} }
override fun loadMore(callback: (message: String) -> Unit) { override fun loadMore(callback: (message: String) -> Unit) {
@ -82,18 +30,10 @@ class AssociationManagementViewModel(application: Application) : ScrollViewModel
} }
/** /**
* TODO 更新社团级别
* *
* @param level * @param level
*/ */
fun update(associationVo: AssociationVo, level: AssociationLevel) { fun update(associationDto: AssociationVo, level: AssociationLevel) {
_data.value?.apply { TODO("更新社团级别")
val i = indexOf(associationVo)
set(i, associationVo.copy(level = level))
val new = mutableListOf<AssociationVo>()
new.addAll(this)
clear()
_data.postValue(new)
}
} }
} }

@ -0,0 +1,81 @@
package com.gyf.csams.main.model
import android.app.Application
import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken
import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.util.*
import kotlinx.coroutines.launch
class AuditAssociationViewModel(application: Application) : ScrollViewModel<DisposeRegInfoVo>(
application
) {
override val initSize: Int = 10
init {
load { }
}
override fun load(callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AssociationApi.Audit), HttpCallback<MutableList<DisposeRegInfoVo>>(
action = "获取审核列表",
onSuccess = { it ->
it.body?.let {
_data.postValue(it)
}
},
typeToken = object :
TypeToken<ApiResponse<MutableList<DisposeRegInfoVo>>>() {}.type
),
jsonParam = OnlyToken(clientType = ClientType.Background)
)
}
}
override fun loadMore(callback: (message: String) -> Unit) {
TODO("Not yet implemented")
}
/**
* 受理注册资料
*
* @param callback
*/
fun accept(regId: Int, isFirstAccept: Boolean, callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AssociationApi.Accept),
HttpCallback<Boolean>(action = "受理社团注册资料", onSuccess = {
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam = AcceptRegAssociation(regId = regId, isFirstAccept = isFirstAccept)
)
}
}
/**
* 提交审核结果
*
* @param regId
* @param result
* @param callback
*/
fun check(regId: Int, cause: String, result: Boolean, callback: (message: String) -> Unit) {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AssociationApi.Check),
HttpCallback<Boolean>(action = "提交审核结果", onSuccess = {
callback(it.message)
}, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam = CheckRegVo(
regId = regId, result = result,
cause = cause
)
)
}
}
}

@ -0,0 +1,11 @@
package com.gyf.csams.main.model
import android.app.Application
import com.gyf.lib.model.SysMessageViewModel
import com.gyf.lib.util.ClientType
class BackgroundViewModel(application: Application) : SysMessageViewModel(application) {
override fun clientType(): ClientType {
return ClientType.Background
}
}

@ -3,17 +3,12 @@ package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.gyf.lib.model.ApplyViewModel import com.gyf.lib.model.ApplyViewModel
import com.gyf.lib.util.ApplyActVo
import com.gyf.lib.util.format import com.gyf.lib.util.format
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomDateTime import com.gyf.lib.util.randomDateTime
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
data class ApplyActVo(
val activityName: String, val activityTime: String,
val location: String, val desc: String,
val size: Int
)
/** /**
* 活动数据管理 * 活动数据管理

@ -1,31 +1,13 @@
package com.gyf.csams.main.model package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import androidx.annotation.IntRange
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.gyf.lib.model.ApplyViewModel import com.gyf.lib.model.ApplyViewModel
import com.gyf.lib.util.QualityReportVo
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.randomChinese
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
const val MAX_SCORE = 5L
/**
* 活动质量汇报单
*
* @property applyName 申请人
* @property activityName 活动名称
* @property merit 优点
* @property defect 缺点
* @property score 星级评价
*/
data class QualityReportVo(
val applyName: String,
val activityName: String,
val merit: String,
val defect: String,
@IntRange(from = 1L, to = MAX_SCORE) val score: Int
)
/** /**
* 活动质量汇报单评价 * 活动质量汇报单评价

@ -1,42 +0,0 @@
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)
)
)
}
}

@ -3,49 +3,14 @@ package com.gyf.csams.main.model
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import com.gyf.lib.util.AllOfficerVo
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.ManagerInfoVo
import com.gyf.lib.util.randomNum
import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch
enum class ColumnType { enum class ColumnType {
Text, Text,
DropMenu DropMenu
} }
/**
* 职务
*
* @property desc
*/
enum class Duty(val desc: String) {
Minister("部长"),
Manager("干事")
}
/**
* 人员信息
*
* @property name 名字
* @property studentId 学号
* @property mobile 手机号
* @property duty 职务
* @property counselor 导员
*/
data class OfficerVo(
val name: String,
val studentId: String,
val mobile: String,
val duty: Duty,
val counselor: String
)
data class AllOfficerVo(
val secretariat: MutableList<OfficerVo>,
val propaganda: MutableList<OfficerVo>,
val publicRelationsDepartment: MutableList<OfficerVo>
)
/** /**
* 部门干事数据状态管理 * 部门干事数据状态管理
@ -55,69 +20,20 @@ class ManagementOfficerModel : ViewModel() {
private val _data = MutableLiveData<AllOfficerVo>() private val _data = MutableLiveData<AllOfficerVo>()
val data: LiveData<AllOfficerVo> = _data val data: LiveData<AllOfficerVo> = _data
init { init {
load() load()
} }
private fun replace(
list: MutableList<OfficerVo>,
index: Int,
callback: (s: MutableList<OfficerVo>) -> Unit
) {
val s = mutableListOf<OfficerVo>()
list[index] = list[index].copy(duty = Duty.Minister)
s.add(list[index])
s.addAll(list.filter { officerVo -> officerVo != list[index] }
.map { officerVo -> officerVo.copy(duty = Duty.Manager) })
callback(s)
}
fun updateDuty(list: MutableList<OfficerVo>, index: Int) {
_data.value?.apply {
Logger.i("$secretariat")
when (list) {
secretariat -> replace(list = list, index = index) {
_data.postValue(copy(secretariat = it))
}
propaganda -> replace(list = list, index = index) {
_data.postValue(copy(propaganda = it))
}
publicRelationsDepartment -> replace(list = list, index = index) {
_data.postValue(copy(publicRelationsDepartment = it))
}
}
}
fun updateDuty(list: MutableList<ManagerInfoVo>, index: Int) {
TODO("更新职务")
} }
/** /**
* TODO 加载部门成员
* *
*/ */
private fun load() { private fun load() {
viewModelScope.launch { TODO("加载部门成员")
val officerVoList = mutableListOf<OfficerVo>()
val baseSize = 3
val peopleSize = 5
repeat(peopleSize * baseSize) {
officerVoList.add(
OfficerVo(
name = randomChinese(3), studentId = randomNum(8), mobile = randomNum(11),
if (it % peopleSize == 0) Duty.Minister else Duty.Manager, counselor = ""
)
)
}
val all = officerVoList.chunked(peopleSize)
_data.postValue(
AllOfficerVo(
secretariat = all[0].toMutableList(),
propaganda = all[1].toMutableList(),
publicRelationsDepartment = all[2].toMutableList()
)
)
}
} }
} }

@ -1,17 +1,12 @@
package com.gyf.csams.main.model package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import androidx.annotation.IntRange
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.util.ActivityVo
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomDateTime import com.gyf.lib.util.randomDateTime
import com.gyf.lib.util.randomNum import com.gyf.lib.util.randomNum
import java.util.*
data class ActivityVo(
val activityId: Long, val activityName: String, val association: String,
@IntRange(from = 1, to = MAX_SCORE) val score: Int, val activityTime: Date, val location: String
)
/** /**
* 活动信息数据管理 * 活动信息数据管理

@ -9,7 +9,8 @@ enum class MenuType(val desc: String, val clazz: Map<String, Class<out Activity>
"社团管理", "社团管理",
mapOf( mapOf(
"社团信息管理" to AssociationManagementActivity::class.java, "社团信息管理" to AssociationManagementActivity::class.java,
"审核换名申请表" to RenameActivity::class.java "审核社团换名" to RenameActivity::class.java,
"审核社团注册" to AuditAssociationActivity::class.java
), ),
), ),
Act( Act(

@ -5,29 +5,19 @@ import androidx.lifecycle.viewModelScope
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.RenameVo
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomNum import com.gyf.lib.util.randomNum
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/**
* 换名申请表
*
* @property studentId 学号
* @property oldName 社团原名
* @property newName 社团新名
* @property reason 申请理由
*/
data class RenameVo(
val studentId: String,
val oldName: String,
val newName: String,
val reason: String
)
class RenameViewModel(application: Application) : ScrollViewModel<RenameVo>(application) { class RenameViewModel(application: Application) : ScrollViewModel<RenameVo>(application) {
val approverOrigin = val approverOrigin =
StringForm(formDesc = application.getString(R.string.approver_origin), textLength = 30) StringForm(
formDesc = application.getString(R.string.first_approver_origin),
textLength = 30
)
override val initSize: Int = 10 override val initSize: Int = 10

@ -16,10 +16,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.main.model.AssociationLevel
import com.gyf.csams.main.model.AssociationManagementViewModel import com.gyf.csams.main.model.AssociationManagementViewModel
import com.gyf.lib.uikit.Body import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainBoxFrame import com.gyf.lib.uikit.MainBoxFrame
import com.gyf.lib.util.AssociationLevel
/** /**
* 社团管理 * 社团管理
@ -52,7 +52,7 @@ class AssociationManagementActivity : ComponentActivity() {
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.SpaceBetween
) { ) {
Text( Text(
text = "$associationId", text = "$id",
style = MaterialTheme.typography.h6 style = MaterialTheme.typography.h6
) )
Text( Text(
@ -68,7 +68,9 @@ class AssociationManagementActivity : ComponentActivity() {
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Text( Text(
text = commander, text = ""
/**TODO 用户**/
,
style = MaterialTheme.typography.h5 style = MaterialTheme.typography.h5
) )
Text( Text(
@ -80,7 +82,7 @@ class AssociationManagementActivity : ComponentActivity() {
expanded = true expanded = true
}) { }) {
Text( Text(
text = level.name, text = level?.name ?: "暂无评级",
style = MaterialTheme.typography.h5 style = MaterialTheme.typography.h5
) )
DropdownMenu( DropdownMenu(
@ -89,7 +91,7 @@ class AssociationManagementActivity : ComponentActivity() {
AssociationLevel.values().forEach { AssociationLevel.values().forEach {
DropdownMenuItem(onClick = { DropdownMenuItem(onClick = {
model.update( model.update(
associationVo = this@apply, associationDto = this@apply,
level = it level = it
) )
expanded = false expanded = false

@ -0,0 +1,310 @@
package com.gyf.csams.main.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.coil.rememberCoilPainter
import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.google.accompanist.insets.navigationBarsWithImePadding
import com.gyf.csams.R
import com.gyf.csams.main.model.AuditAssociationViewModel
import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTableImeSimple
import com.gyf.lib.uikit.*
import com.gyf.lib.util.*
class AuditAssociationActivity : ComponentActivity() {
@ExperimentalMaterialApi
@ExperimentalAnimatedInsets
@ExperimentalComposeApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
ImeBody {
val model: AuditAssociationViewModel = viewModel()
val data by model.data.observeAsState()
TestTableImeSimple(
title = R.string.association_reg_title
) {
data?.forEach {
item {
RegisterForm(vo = it)
Spacer(modifier = Modifier.height(10.dp))
}
}
}
}
}
}
@Composable
private fun CheckForm(
vo: DisposeRegInfoVo,
scaffoldModel: ScaffoldModel = viewModel(),
auditAssociationViewModel: AuditAssociationViewModel = viewModel(),
first: @Composable () -> Unit,
last: @Composable () -> Unit
) {
val baseHeight = 50.dp
(TokenManager.getOwnInfo() as? ManagerVo)?.let { it ->
var confirmDesc: Int? = null
var backDesc: Int? = null
var onConfirm: (() -> Unit)? = null
var onBack: (() -> Unit)? = null
val check: (result: Boolean, cause: StringForm) -> Unit =
{ result: Boolean, cause: StringForm ->
scaffoldModel.update(
message = "确认${if (result) "上报" else "驳回"}",
actionLabel = "确认"
) {
auditAssociationViewModel.check(
regId = vo.log.id,
result = result,
cause = cause.formValue.value
?: throw IllegalArgumentException("无法获取审核理由")
) {
scaffoldModel.update(message = it, actionLabel = "刷新") {
auditAssociationViewModel.load { }
}
}
}
}
val accept: (m: String, isFirstAccept: Boolean) -> Unit =
{ m: String, isFirstAccept: Boolean ->
scaffoldModel.update("确认${m}", actionLabel = "确认") {
auditAssociationViewModel.accept(
regId = vo.log.id,
isFirstAccept = isFirstAccept
) {
scaffoldModel.update(message = it, actionLabel = "刷新") {
auditAssociationViewModel.load { }
}
}
}
}
val doCheck: @Composable () -> Unit = {
val cause = ValidStringForm(
formDesc = "审核理由",
textLength = 20
)
val statusForm by cause.statusForm.observeAsState()
onConfirm = {
if (statusForm == FormStatus.Empty) {
scaffoldModel.update("${cause.formDesc}不能为空", actionLabel = "知道了")
} else {
check(true, cause)
}
}
onBack = {
if (statusForm == FormStatus.Empty) {
scaffoldModel.update("${cause.formDesc}不能为空", actionLabel = "知道了")
} else {
check(false, cause)
}
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
BaseTextField(
modifier = Modifier.navigationBarsWithImePadding(),
form = cause
)
}
}
when {
//初审记录,负责人为空 等待初审
vo.log.nextAudit == null && vo.log.manager == null -> {
if (it.duty == Duty.PamphaBhusal) {
confirmDesc = R.string.accept_btn
onConfirm = { accept("受理", true) }
}
first()
RowItem(key = R.string.first_approver_origin, value = "")
RowItem(key = R.string.first_result, value = "")
last()
RowItem(key = R.string.last_approver_origin, value = "")
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "等待初审"
)
}
//初审记录,负责人不为空 初审受理
vo.log.nextAudit == null && vo.log.manager != null -> {
first()
if (it.duty == Duty.PamphaBhusal) {
confirmDesc = R.string.reported_btn
backDesc = R.string.reject_btn
doCheck()
} else {
RowItem(key = R.string.first_approver_origin, value = "")
}
RowItem(key = R.string.first_result, value = "")
last()
RowItem(key = R.string.last_approver_origin, value = "")
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "初审受理"
)
}
//初审记录,审核通过(上报) 等待复审
vo.log.nextAudit != null && vo.log.nextAudit?.manager == null -> {
if (it.duty == Duty.Teacher) {
confirmDesc = R.string.recheck_btn
onConfirm = { accept("复审", false) }
}
first()
RowItem(key = R.string.first_approver_origin, value = vo.log.cause)
RowItem(key = R.string.first_result, value = "通过")
last()
RowItem(key = R.string.last_approver_origin, value = "")
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "等待复审"
)
}
//复审记录,审核结果为空 复审受理
vo.log.nextAudit != null && vo.log.nextAudit?.result == null -> {
first()
RowItem(key = R.string.first_approver_origin, value = vo.log.cause)
RowItem(key = R.string.first_result, value = "通过")
last()
if (it.duty == Duty.Teacher) {
confirmDesc = R.string.allow_btn
backDesc = R.string.reject_btn
doCheck()
} else {
RowItem(key = R.string.last_approver_origin, value = "")
}
RowItem(key = R.string.last_result, value = "")
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "复审受理"
)
}
else -> {
first()
RowItem(key = R.string.first_approver_origin, value = vo.log.cause)
RowItem(
key = R.string.first_result, value = when (vo.log.result) {
null -> ""
true -> "通过"
false -> "不通过"
}
)
last()
RowItem(
key = R.string.last_approver_origin,
value = vo.log.nextAudit?.cause ?: ""
)
RowItem(
key = R.string.last_result, value = when (vo.log.nextAudit?.result) {
null -> ""
true -> "通过"
false -> "不通过"
}
)
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.audit_phases, value = "完成审核"
)
}
}
if (confirmDesc != null) {
BottomButton(
confirmDesc = confirmDesc,
backDesc = backDesc,
modifier = Modifier.fillMaxWidth(),
onBack = onBack
) {
onConfirm?.let { it() }
}
}
}
}
@Composable
private fun RegisterForm(
modifier: Modifier = Modifier, vo: DisposeRegInfoVo
) {
Column(
modifier = modifier.border(
width = 1.dp,
color = MaterialTheme.colors.onBackground
)
) {
val baseHeight = 50.dp
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.petitioner,
value = vo.log.user.name
)
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.association_name,
value = vo.name
)
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.association_desc,
value = vo.desc
)
RowItem(
modifier = Modifier.height(baseHeight * 3),
key = R.string.association_logo
) {
//TODO 图片全屏显示
val painter =
rememberCoilPainter("${com.gyf.lib.BuildConfig.SERVER_ADDRESS}/${vo.logo}")
Image(painter = painter, contentDescription = null)
}
CheckForm(vo = vo, first = {
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.first_approver,
value = vo.log.manager?.name ?: ""
)
}, last = {
RowItem(
modifier = Modifier.height(baseHeight),
key = R.string.last_approver,
value = vo.log.nextAudit?.manager?.name ?: ""
)
})
}
}
}

@ -13,13 +13,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.ApplyActVo
import com.gyf.csams.main.model.CheckActViewModel import com.gyf.csams.main.model.CheckActViewModel
import com.gyf.csams.uikit.RowItem import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTable import com.gyf.csams.uikit.TestTable
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.ApplyActVo
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
/** /**
@ -27,6 +28,7 @@ import com.gyf.lib.util.BottomButton
* *
*/ */
class CheckActActivity : ComponentActivity() { class CheckActActivity : ComponentActivity() {
@ExperimentalAnimatedInsets
@ExperimentalComposeApi @ExperimentalComposeApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -69,11 +71,14 @@ class CheckActActivity : ComponentActivity() {
value = "${vo.size}" value = "${vo.size}"
) )
RowItem( RowItem(
modifier = Modifier.height(baseHeight), key = R.string.approver, value = "" modifier = Modifier.height(baseHeight), key = R.string.first_approver, value = ""
/**TODO 获取审批人**/ /**TODO 获取审批人**/
) )
RowItem(modifier = Modifier.height(baseHeight * 3), key = R.string.approver_origin) { RowItem(
BaseTextField(modifier = Modifier.fillMaxSize(), form = model.approverOrigin) modifier = Modifier.height(baseHeight * 3),
key = R.string.first_approver_origin
) {
BaseTextField(modifier = Modifier.fillMaxSize(), form = model.approveOrigin)
} }
val message = stringResource(id = R.string.not_impl_error) val message = stringResource(id = R.string.not_impl_error)
BottomButton( BottomButton(

@ -12,21 +12,23 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.CheckQualityReportViewModel import com.gyf.csams.main.model.CheckQualityReportViewModel
import com.gyf.csams.main.model.MAX_SCORE
import com.gyf.csams.main.model.QualityReportVo
import com.gyf.csams.uikit.RowItem import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTable import com.gyf.csams.uikit.TestTable
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.MAX_SCORE
import com.gyf.lib.util.QualityReportVo
/** /**
* 审批质量报告单 * 审批质量报告单
* *
*/ */
class CheckQualityReportActivity : ComponentActivity() { class CheckQualityReportActivity : ComponentActivity() {
@ExperimentalAnimatedInsets
@ExperimentalComposeApi @ExperimentalComposeApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -86,11 +88,14 @@ class CheckQualityReportActivity : ComponentActivity() {
} }
} }
RowItem( RowItem(
modifier = Modifier.height(baseHeight), key = R.string.approver, value = "" modifier = Modifier.height(baseHeight), key = R.string.first_approver, value = ""
/**TODO 获取审批人**/ /**TODO 获取审批人**/
) )
RowItem(modifier = Modifier.height(baseHeight * 3), key = R.string.approver_origin) { RowItem(
BaseTextField(modifier = Modifier.fillMaxSize(), form = model.approverOrigin) modifier = Modifier.height(baseHeight * 3),
key = R.string.first_approver_origin
) {
BaseTextField(modifier = Modifier.fillMaxSize(), form = model.approveOrigin)
} }
val message = stringResource(id = R.string.not_impl_error) val message = stringResource(id = R.string.not_impl_error)
BottomButton( BottomButton(

@ -9,19 +9,18 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.OutlinedButton import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.account.model.LoginViewModel import com.gyf.csams.account.model.LoginViewModel
import com.gyf.csams.main.model.MainViewModel
import com.gyf.csams.main.model.MenuType import com.gyf.csams.main.model.MenuType
import com.gyf.lib.service.BaseActivity import com.gyf.lib.service.BaseActivity
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
import com.gyf.lib.util.ClientType import com.gyf.lib.util.ClientType
import com.gyf.lib.util.ManagerInfoVo
import com.gyf.lib.util.TokenManager
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
@ -32,22 +31,33 @@ class MainActivity : BaseActivity() {
setContent { setContent {
Body { Body {
val model: MainViewModel = viewModel()
val loginViewModel: LoginViewModel = viewModel() val loginViewModel: LoginViewModel = viewModel()
val scaffoldModel: ScaffoldModel = viewModel() val scaffoldModel: ScaffoldModel = viewModel()
val person by model.person.observeAsState()
MainColumnFrame(background = { /*TODO*/ }) { MainColumnFrame(background = { /*TODO*/ }) {
person?.let { (TokenManager.getPersonInfo() as? ManagerInfoVo)?.let {
Profile( Profile(
modifier = Modifier modifier = Modifier
.weight(0.3F) .weight(0.3F)
.padding(10.dp), personInfoVo = it .padding(10.dp),
personInfoVo = it
) )
} }
Column( Column(
modifier = Modifier.weight(0.7F), modifier = Modifier.weight(0.7F),
verticalArrangement = Arrangement.SpaceEvenly verticalArrangement = Arrangement.SpaceEvenly
) { ) {
OutlinedButton(onClick = {
startActivity(
Intent(
this@MainActivity,
NotificationActivity::class.java
)
)
}, modifier = Modifier.fillMaxWidth()) {
Text(text = "我的通知")
}
OutlinedButton(onClick = { OutlinedButton(onClick = {
startActivity(Intent(this@MainActivity, DepartmentActivity::class.java)) startActivity(Intent(this@MainActivity, DepartmentActivity::class.java))
}, modifier = Modifier.fillMaxWidth()) { }, modifier = Modifier.fillMaxWidth()) {

@ -21,14 +21,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.Duty
import com.gyf.csams.main.model.ManagementOfficerModel import com.gyf.csams.main.model.ManagementOfficerModel
import com.gyf.csams.main.model.MinisterVo
import com.gyf.csams.main.model.OfficerVo
import com.gyf.lib.uikit.Body import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.Profile import com.gyf.lib.uikit.Profile
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.ManagerInfoVo
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
/** /**
@ -99,7 +97,7 @@ class ManagementOfficerActivity : ComponentActivity() {
model: ManagementOfficerModel = viewModel(), model: ManagementOfficerModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel(), scaffoldModel: ScaffoldModel = viewModel(),
@StringRes id: Int, @StringRes id: Int,
officerVoList: MutableList<OfficerVo>, officerVoList: MutableList<ManagerInfoVo>,
) { ) {
Column( Column(
modifier = modifier modifier = modifier
@ -124,19 +122,13 @@ class ManagementOfficerActivity : ComponentActivity() {
.width(200.dp) .width(200.dp)
.fillMaxHeight() .fillMaxHeight()
.border(width = 1.dp, color = MaterialTheme.colors.onBackground), .border(width = 1.dp, color = MaterialTheme.colors.onBackground),
personInfoVo = personInfoVo = it.value
MinisterVo(
name = it.value.name,
duty = it.value.duty.desc,
headImg = "",
desc = it.value.mobile
)
) { ) {
Logger.i("expanded=$expanded") Logger.i("expanded=$expanded")
Text( Text(
text = it.value.duty.desc, text = it.value.duty.desc,
modifier = Modifier.clickable(onClick = { modifier = Modifier.clickable(onClick = {
if (it.value.duty.desc != Duty.Minister.desc) expanded = if (it.value.duty.isOfficer()) expanded =
true else scaffoldModel true else scaffoldModel
.update(message = context.getString(R.string.update_duty_error)) .update(message = context.getString(R.string.update_duty_error))
}) })
@ -149,7 +141,7 @@ class ManagementOfficerActivity : ComponentActivity() {
model.updateDuty(list = officerVoList, index = it.index) model.updateDuty(list = officerVoList, index = it.index)
expanded = false expanded = false
}) { }) {
Text(text = Duty.Minister.desc) Text(text = it.value.duty.desc)
} }
} }
} }

@ -13,12 +13,13 @@ import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.ActivityVo
import com.gyf.csams.main.model.MAX_SCORE
import com.gyf.csams.main.model.ManagerActViewModel import com.gyf.csams.main.model.ManagerActViewModel
import com.gyf.csams.uikit.RowItem import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTable import com.gyf.csams.uikit.TestTable
import com.gyf.lib.util.ActivityVo
import com.gyf.lib.util.MAX_SCORE
import com.gyf.lib.util.format import com.gyf.lib.util.format
/** /**
@ -26,6 +27,7 @@ import com.gyf.lib.util.format
* *
*/ */
class ManagerActActivity : ComponentActivity() { class ManagerActActivity : ComponentActivity() {
@ExperimentalAnimatedInsets
@ExperimentalComposeApi @ExperimentalComposeApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

@ -0,0 +1,80 @@
package com.gyf.csams.main.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
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.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.main.model.BackgroundViewModel
import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.util.NotificationVo
import com.gyf.lib.util.format
import java.util.*
/**
* 通知
*
*/
class NotificationActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Body {
val model: BackgroundViewModel = viewModel()
val data by model.data.observeAsState()
MainColumnFrame(background = { /*TODO*/ }) {
if (data?.size == 0) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
Text(text = "目前没有收到任何通知")
}
}
LazyColumn {
data?.forEach {
item {
MessageItem(content = it)
}
}
}
}
}
}
}
@Composable
private fun MessageItem(modifier: Modifier = Modifier, content: NotificationVo) {
Card(modifier = modifier, backgroundColor = MaterialTheme.colors.background) {
Column(modifier = Modifier.padding(10.dp)) {
Text(text = content.title, style = MaterialTheme.typography.h5)
Spacer(modifier = Modifier.height(5.dp))
Card(
backgroundColor = MaterialTheme.colors.background,
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
Text(text = content.content)
}
Spacer(modifier = Modifier.height(10.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
Text(text = Date(content.createTime).format())
}
}
}
}
}

@ -13,17 +13,19 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.main.model.RenameViewModel import com.gyf.csams.main.model.RenameViewModel
import com.gyf.csams.main.model.RenameVo
import com.gyf.csams.uikit.RowItem import com.gyf.csams.uikit.RowItem
import com.gyf.csams.uikit.TestTable import com.gyf.csams.uikit.TestTable
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.BaseTextField
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.RenameVo
class RenameActivity : ComponentActivity() { class RenameActivity : ComponentActivity() {
@ExperimentalAnimatedInsets
@ExperimentalComposeApi @ExperimentalComposeApi
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -63,10 +65,13 @@ class RenameActivity : ComponentActivity() {
value = renameVo.reason value = renameVo.reason
) )
RowItem( RowItem(
modifier = Modifier.height(baseHeight), key = R.string.approver, value = "" modifier = Modifier.height(baseHeight), key = R.string.first_approver, value = ""
/**TODO 获取审批人**/ /**TODO 获取审批人**/
) )
RowItem(modifier = Modifier.height(baseHeight * 3), key = R.string.approver_origin) { RowItem(
modifier = Modifier.height(baseHeight * 3),
key = R.string.first_approver_origin
) {
BaseTextField(modifier = Modifier.fillMaxSize(), form = model.approverOrigin) BaseTextField(modifier = Modifier.fillMaxSize(), form = model.approverOrigin)
} }
val message = stringResource(id = R.string.not_impl_error) val message = stringResource(id = R.string.not_impl_error)

@ -2,25 +2,27 @@ package com.gyf.csams.uikit
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.MaterialTheme import androidx.compose.material.*
import androidx.compose.material.Text import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.insets.*
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.Body import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
/** /**
* 表格 * 表格
* *
@ -65,6 +67,53 @@ fun <A> TestTable(
} }
} }
@ExperimentalAnimatedInsets
@ExperimentalMaterialApi
@Composable
fun TestTableImeSimple(
modifier: Modifier = Modifier,
@StringRes title: Int? = null,
content: LazyListScope.() -> Unit
) {
Column(modifier = modifier) {
title?.let {
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.border(width = 1.dp, color = MaterialTheme.colors.onBackground),
horizontalArrangement = Arrangement.Center,
) {
Text(
text = stringResource(id = title),
style = MaterialTheme.typography.h4
)
}
}
val listState = rememberLazyListState()
val insets = LocalWindowInsets.current
val imeBottom = with(LocalDensity.current) { insets.ime.bottom.toDp() }
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState,
content = content
)
LaunchedEffect(imeBottom) {
listState.scrollBy(imeBottom.value)
}
}
}
/**
* 表格行
*
* @param modifier
* @param key
* @param value
* @param content
*/
/** /**
* 表格行 * 表格行
* *

@ -26,4 +26,9 @@
<string name="activity_id">活动编号</string> <string name="activity_id">活动编号</string>
<string name="activity_association">活动社团</string> <string name="activity_association">活动社团</string>
<string name="activity_location">活动地点</string> <string name="activity_location">活动地点</string>
<string name="accept_btn">受理</string>
<string name="allow_btn">通过</string>
<string name="last_approver">复审负责人</string>
<string name="first_result">初审结果</string>
<string name="last_result">复审结果</string>
</resources> </resources>

@ -26,4 +26,9 @@
<string name="activity_id">活动编号</string> <string name="activity_id">活动编号</string>
<string name="activity_association">活动社团</string> <string name="activity_association">活动社团</string>
<string name="activity_location">活动地点</string> <string name="activity_location">活动地点</string>
<string name="accept_btn">受理</string>
<string name="allow_btn">通过</string>
<string name="last_approver">复审负责人</string>
<string name="first_result">初审结果</string>
<string name="last_result">复审结果</string>
</resources> </resources>

@ -26,4 +26,9 @@
<string name="activity_id">活动编号</string> <string name="activity_id">活动编号</string>
<string name="activity_association">活动社团</string> <string name="activity_association">活动社团</string>
<string name="activity_location">活动地点</string> <string name="activity_location">活动地点</string>
<string name="accept_btn">受理</string>
<string name="allow_btn">通过</string>
<string name="last_approver">复审负责人</string>
<string name="first_result">初审结果</string>
<string name="last_result">复审结果</string>
</resources> </resources>

@ -1,5 +1,8 @@
package com.gyf.csams package com.gyf.csams
import com.google.gson.reflect.TypeToken
import com.gyf.lib.util.ApiResponse
import com.orhanobut.logger.Logger
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
@ -13,4 +16,14 @@ class ExampleUnitTest {
fun addition_isCorrect() { fun addition_isCorrect() {
assertEquals(4, 2 + 2) assertEquals(4, 2 + 2)
} }
@Test
fun typeToken() {
initToken<Boolean>()
}
inline fun <reified E> initToken() {
val typeToken = object : TypeToken<ApiResponse<E>>() {}.type
Logger.i("typeToken=$typeToken")
}
} }

@ -3,6 +3,7 @@
package="com.gyf.csams"> package="com.gyf.csams">
<!--访问网络--> <!--访问网络-->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- 这个权限用于进行网络定位--> <!-- 这个权限用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 这个权限用于访问GPS定位--> <!-- 这个权限用于访问GPS定位-->

@ -1,14 +1,20 @@
package com.gyf.csams package com.gyf.csams
import android.app.Activity import android.app.Activity
import com.google.gson.reflect.TypeToken
import com.gyf.csams.account.ui.AccountActivity import com.gyf.csams.account.ui.AccountActivity
import com.gyf.csams.main.ui.MainActivity import com.gyf.csams.main.ui.MainActivity
import com.gyf.lib.uikit.AbstractInitActivity import com.gyf.lib.uikit.AbstractInitActivity
import com.gyf.lib.util.AccountApi import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.UserVo
import java.lang.reflect.Type
class InitActivity : AbstractInitActivity() { class InitActivity : AbstractInitActivity<UserVo>() {
override val main: Class<out Activity> = MainActivity::class.java override val main: Class<out Activity> = MainActivity::class.java
override val login: Class<out Activity> = AccountActivity::class.java override val login: Class<out Activity> = AccountActivity::class.java
override val api: AccountApi = AccountApi.ForegroundToken override val api: AccountApi = AccountApi.ForegroundToken
override val typeToken: Type = object : TypeToken<ApiResponse<UserVo>>() {}.type
} }

@ -10,7 +10,6 @@ import com.google.gson.reflect.TypeToken
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.account.ui.AccountActivity import com.gyf.csams.account.ui.AccountActivity
import com.gyf.csams.account.ui.AccountRoute import com.gyf.csams.account.ui.AccountRoute
import com.gyf.csams.util.SimpleCallback
import com.gyf.lib.model.AbstractLoginViewModel import com.gyf.lib.model.AbstractLoginViewModel
import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.uikit.ValidStringForm
@ -19,37 +18,14 @@ import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/**
* 响应自动生成密码
*
* @property password
*/
data class UserResDto(val password: String)
/**
* 构造登录注册信息实体表单
*
* @property studentId 学号
* @property name 姓名
*/
data class UserVo(val studentId: String, val name: String)
/**
* 用户登陆表单
*
* @property studentId 学号
* @property password 密码
* @property device 设备型号
*/
data class UserLoginVo(val studentId: String, val password: String, val device: String)
/** /**
* 密码弹窗信息 * 密码弹窗信息
* *
* @property message * @property message
* @property userResDto * @property userPasswordVo
*/ */
data class DialogMessage(val message: String, val userResDto: UserResDto?) data class DialogMessage(val message: String, val userPasswordVo: UserPasswordVo?)
/** /**
* 注册表单 * 注册表单
@ -139,7 +115,7 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
val url = Api.buildUrl(AccountApi.CheckId) val url = Api.buildUrl(AccountApi.CheckId)
Logger.i("检测${id.formDesc},请求接口$url") Logger.i("检测${id.formDesc},请求接口$url")
HttpClient.get( HttpClient.get(
url, SimpleCallback<Boolean>( url, HttpCallback<Boolean>(
action = "${id.formDesc}重复检测", action = "${id.formDesc}重复检测",
onSuccess = { onSuccess = {
if (it.body == true) { if (it.body == true) {
@ -150,7 +126,7 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
checkForm() checkForm()
}, },
onFail = { Logger.e(it) }, onFail = { Logger.e(it) },
type = object : TypeToken<ApiResponse<Boolean>>() {}.type typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type
), mapOf("studentId" to "${id.formValue.value}") ), mapOf("studentId" to "${id.formValue.value}")
) )
} }
@ -188,22 +164,22 @@ class AccountViewModel(application: Application) : AbstractLoginViewModel(applic
val url = Api.buildUrl(AccountApi.Register) val url = Api.buildUrl(AccountApi.Register)
Logger.i("开始$regBtnDesc,请求接口:$url") Logger.i("开始$regBtnDesc,请求接口:$url")
HttpClient.post( HttpClient.post(
url, SimpleCallback<UserResDto>( url, HttpCallback<UserPasswordVo>(
action = regBtnDesc, action = regBtnDesc,
onSuccess = { onSuccess = {
_dialogMsg.postValue( _dialogMsg.postValue(
DialogMessage( DialogMessage(
message = it.message, message = it.message,
userResDto = it.body userPasswordVo = it.body
) )
) )
}, },
onFail = onFail, onFail = onFail,
type = object : TypeToken<ApiResponse<UserResDto>>() {}.type typeToken = object : TypeToken<ApiResponse<UserPasswordVo>>() {}.type
), ),
jsonParam = UserVo( jsonParam = UserRegVo(
studentId = "${id.formValue.value}", studentId = id.formValue.value ?: throw IllegalArgumentException("学号为空"),
name = "${name.formValue.value}" name = name.formValue.value ?: throw IllegalArgumentException("姓名为空")
) )
) )
resetForm() resetForm()

@ -41,7 +41,7 @@ class AccountActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
TestBody { nav, scaffoldState -> NavBody { nav, scaffoldState ->
NavHost(navController = nav, startDestination = AccountRoute.Login.name) { NavHost(navController = nav, startDestination = AccountRoute.Login.name) {
composable(AccountRoute.Login.name) { composable(AccountRoute.Login.name) {
Account( Account(
@ -241,7 +241,7 @@ class AccountActivity : ComponentActivity() {
private fun RegisterDialog(accountViewModel: AccountViewModel = viewModel()) { private fun RegisterDialog(accountViewModel: AccountViewModel = viewModel()) {
val dialogMsg: DialogMessage? by accountViewModel.dialogMsg.observeAsState(null) val dialogMsg: DialogMessage? by accountViewModel.dialogMsg.observeAsState(null)
val message = dialogMsg?.userResDto?.password val message = dialogMsg?.userPasswordVo?.password
if (message?.isNotEmpty() == true) { if (message?.isNotEmpty() == true) {
PasswordDialog(message = message) PasswordDialog(message = message)
} }

@ -10,13 +10,9 @@ import com.gyf.csams.uikit.ActivityDetailMenu
import com.gyf.csams.uikit.TopMenuInterface import com.gyf.csams.uikit.TopMenuInterface
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.NOT_IMPL_TIP import com.gyf.lib.util.*
import com.gyf.lib.util.randomChinese
import com.gyf.lib.util.randomDateTime
import com.gyf.lib.util.randomNum
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.*
/** /**
* 活动详情菜单通用状态 * 活动详情菜单通用状态
@ -27,21 +23,6 @@ class ActivityDetailViewModel : ViewModel(), TopMenuInterface<ActivityDetailMenu
override val currentMenu: LiveData<ActivityDetailMenu> = _currentMenu override val currentMenu: LiveData<ActivityDetailMenu> = _currentMenu
} }
/**
* TODO
*
* @property associationName 社团名字
* @property activityName 活动名
* @property activityTime 活动时间
* @property activityLocation 活动地点
* @property activityDesc 活动介绍
*/
data class ActivityDetailVo(
val associationName: String, val activityName: String,
val activityTime: Date, val activityLocation: String,
val activityDesc: String
)
/** /**
* 活动信息 * 活动信息
@ -91,23 +72,6 @@ class ActivityInfoViewModel : ViewModel() {
} }
/**
* 图片
* @property name 文件名
* @property size 文件大小
* @property url 文件路径
* @property md5 文件hash
* @property createTime 文件创建时间
* @property studentId 文件上传人
*/
data class ActivityPhotoVo(
val name: String,
val size: Long,
val url: String,
val md5: String,
val createTime: Date,
val studentId: String
)
/** /**
* 活动相册数据状态管理 * 活动相册数据状态管理
@ -179,12 +143,6 @@ class ActivityPhotoViewModel(application: Application) :
} }
} }
data class ActivityMemberVo(val studentId: String, val name: String)
data class ActivityMembersVo(
val organizer: ActivityMemberVo,
val participant: MutableList<ActivityMemberVo>?
)
class ActivityMemberViewModel(application: Application) : class ActivityMemberViewModel(application: Application) :
ScrollViewModel<ActivityMemberVo>(application) { ScrollViewModel<ActivityMemberVo>(application) {
@ -245,11 +203,6 @@ class ActivityMemberViewModel(application: Application) :
} }
} }
data class BBSVo(val studentId: String, val name: String, val createTime: Date, val content: String)
class T
class BBSCommentModel : AbstractComment() { class BBSCommentModel : AbstractComment() {
override val newContent = ValidStringForm(formDesc = "评论内容", textLength = 80) override val newContent = ValidStringForm(formDesc = "评论内容", textLength = 80)

@ -24,9 +24,12 @@ import com.gyf.csams.R
import com.gyf.csams.activity.model.* import com.gyf.csams.activity.model.*
import com.gyf.csams.uikit.* import com.gyf.csams.uikit.*
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.NavBody
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.uikit.ShowSnackbar import com.gyf.lib.uikit.ShowSnackbar
import com.gyf.lib.uikit.TestBody import com.gyf.lib.util.ActivityMemberVo
import com.gyf.lib.util.ActivityPhotoVo
import com.gyf.lib.util.BBSVo
import com.gyf.lib.util.format import com.gyf.lib.util.format
/** /**
@ -41,7 +44,7 @@ class ActivityDetailActivity : ComponentActivity() {
setContent { setContent {
TestBody { nav, scaffoldState -> NavBody { nav, scaffoldState ->
val model: ActivityDetailViewModel = viewModel() val model: ActivityDetailViewModel = viewModel()
val currentMenuName by model.currentMenu.observeAsState(ActivityDetailMenu.startMenu) val currentMenuName by model.currentMenu.observeAsState(ActivityDetailMenu.startMenu)
Column { Column {

@ -9,7 +9,10 @@ import com.gyf.csams.uikit.AssociationMenu
import com.gyf.csams.uikit.TopMenuInterface import com.gyf.csams.uikit.TopMenuInterface
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.HistoryActVo
import com.gyf.lib.util.MemberVo
import com.gyf.lib.util.NOT_IMPL_TIP import com.gyf.lib.util.NOT_IMPL_TIP
import com.gyf.lib.util.OngoingActVo
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -42,8 +45,6 @@ class AssociationViewModel : ViewModel(), TopMenuInterface<AssociationMenu> {
} }
data class MemberVo(val name: String)
/** /**
* 社团会员 * 社团会员
* *
@ -111,16 +112,11 @@ class MemberViewModel(application: Application) : ScrollViewModel<MemberVo>(appl
} }
} }
data class OngoingActVo(val name: String)
class OngoingActViewModel : ViewModel() { class OngoingActViewModel : ViewModel() {
private val _act = MutableLiveData<OngoingActVo>() private val _act = MutableLiveData<OngoingActVo>()
val act: LiveData<OngoingActVo> = _act val act: LiveData<OngoingActVo> = _act
} }
data class HistoryActVo(val name: String)
/** /**
* 历史活动 * 历史活动

@ -6,7 +6,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.NOT_IMPL_TIP import com.gyf.lib.util.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.random.Random import kotlin.random.Random
@ -22,49 +22,6 @@ enum class ExamActivityType(val menuName: String) {
JOIN_Association("入团申请表") JOIN_Association("入团申请表")
} }
/**
* 题型
*
*/
enum class ExamType(val type: String) {
//选择题
CQ("选择题"),
//开放题
OQ("开放题")
}
sealed class Exam {
abstract val examType: ExamType
abstract val question: StringForm
}
/**
* 开放题
*
* @property examType 题型描述
* @property question 问题
*/
data class OpenQuestionsVo(
override val examType: ExamType = ExamType.OQ, override val question: StringForm
) : Exam()
/**
* 选择题
*
* @property examType 题型描述
* @property answers 答案
* @property rightAnswer 正确答案
* @property question 问题
*/
data class ChoiceQuestionVo(
override val examType: ExamType = ExamType.CQ,
val answers: List<StringForm>,
val rightAnswer: Int,
override val question: StringForm
) : Exam()
/** /**

@ -1,31 +1,40 @@
package com.gyf.csams.association.model package com.gyf.csams.association.model
import android.app.Application import android.app.Application
import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import coil.imageLoader
import coil.request.ImageRequest
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.MainApplication import com.gyf.csams.MainApplication
import com.gyf.csams.util.SimpleCallback import com.gyf.lib.BuildConfig
import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.* import com.gyf.lib.util.*
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
data class RegAssociationVo(val name: String, val desc: String, val fileId: Int, val token: Token) class TestStringForm(formDesc: String, textLength: Int) : ValidStringForm(formDesc, textLength) {
fun setValue(value: String) {
_formValue.postValue(value)
}
}
class RegAssociationViewModel(application: Application) : AndroidViewModel(application) { class RegAssociationViewModel(application: Application) : AndroidViewModel(application) {
val frameDesc = "社团注册资料" val frameDesc = "社团注册资料"
val name = ValidStringForm(formDesc = "社团名称", textLength = 5) val name = TestStringForm(formDesc = "社团名称", textLength = 5)
val desc = ValidStringForm(formDesc = "社团简介", textLength = 30) val desc = TestStringForm(formDesc = "社团简介", textLength = 30)
private val _picture = MutableLiveData<Uri?>() private val _picture = MutableLiveData<Uri?>()
val picture: LiveData<Uri?> = _picture val picture: LiveData<Uri?> = _picture
@ -33,10 +42,17 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
private val _fileId = MutableLiveData<Int>() private val _fileId = MutableLiveData<Int>()
val fileId: LiveData<Int> = _fileId val fileId: LiveData<Int> = _fileId
private val _checkInfo = MutableLiveData<AssociationCheckVo?>()
val checkInfo: LiveData<AssociationCheckVo?> = _checkInfo
val picturePlaceHolder = "请上传图片" val picturePlaceHolder = "请上传图片"
val errorPicture = "图片加载失败,请联系管理员" val errorPicture = "图片加载失败,请联系管理员"
init {
read()
}
fun setPicture(uri: Uri) { fun setPicture(uri: Uri) {
_picture.value = uri _picture.value = uri
} }
@ -49,40 +65,84 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
throw IllegalArgumentException(UNKNOW_ERROR) throw IllegalArgumentException(UNKNOW_ERROR)
} }
/**
* 加载历史提交的注册资料可能为空
*
*/
private fun read() {
viewModelScope.launch {
HttpClient.post(
Api.buildUrl(AssociationApi.Read),
HttpCallback<AssociationCheckVo>(action = "加载历史注册资料", onSuccess = { it ->
it.body?.let { it ->
name.setValue(it.name)
desc.setValue(it.desc)
_checkInfo.postValue(it)
val context = getApplication<Application>()
val request = ImageRequest.Builder(context)
.data("${BuildConfig.SERVER_ADDRESS}/${it.logo}")
.target(
onSuccess = { result ->
it.logo.split("/").apply {
File.createTempFile(last(), null, context.cacheDir).apply {
Logger.d("文件路径:${absolutePath}")
FileOutputStream(this).use {
result.toBitmap().compress(
Bitmap.CompressFormat.JPEG,
100,
it
)
}
}
.apply {
Logger.i("读取缓存图片")
setPicture(Uri.fromFile(this))
_fileId.postValue(it.fileId)
}
}
}
)
.build()
context.imageLoader.enqueue(request)
}
},
typeToken = object : TypeToken<ApiResponse<AssociationCheckVo>>() {}.type
),
jsonParam = OnlyToken(clientType = ClientType.Foreground)
)
}
}
fun uploadPhoto(callback: (value: String) -> Unit) { fun uploadPhoto(callback: (value: String) -> Unit) {
getInputSteam()?.readBytes()?.apply { getInputSteam()?.readBytes()?.apply {
val token = TokenManager.token viewModelScope.launch {
if (token != null) { val context = getApplication<MainApplication>()
viewModelScope.launch { runCatching {
val context = getApplication<MainApplication>() val cacheFile = File(context.cacheDir, "${System.currentTimeMillis()}")
cacheFile.writeBytes(this@apply)
runCatching {
val cacheFile = File(context.cacheDir, "${System.currentTimeMillis()}") HttpClient.uploadFile(
cacheFile.writeBytes(this@apply) Api.buildUrl(AssociationApi.Logo),
HttpCallback<List<Int>>("上传图片", onSuccess = {
HttpClient.uploadFile( Logger.i(it.message)
Api.buildUrl(AssociationApi.Logo), callback(it.message)
SimpleCallback<List<Int>>("上传图片", onSuccess = { it.body?.let {
Logger.i(it.message) _fileId.postValue(it.first())
callback(it.message) }
it.body?.let {
_fileId.postValue(it.first()) }, onFail = {
} Logger.e(it)
callback("图片上传失败")
}, onFail = { }, typeToken = object : TypeToken<ApiResponse<List<Int>>>() {}.type),
Logger.e(it) fileList = arrayOf(cacheFile)
callback("图片上传失败") )
}, type = object : TypeToken<ApiResponse<List<Int>>>() {}.type),
token = token,
fileList = arrayOf(cacheFile)
)
}
} }
} else {
callback(UNKNOW_ERROR)
} }
} }
} }
@ -94,15 +154,14 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
fun register(callback: (value: String) -> Unit) { fun register(callback: (value: String) -> Unit) {
val nameValue = name.formValue.value val nameValue = name.formValue.value
val descValue = desc.formValue.value val descValue = desc.formValue.value
val token = TokenManager.token
val fileId = _fileId.value val fileId = _fileId.value
if (token != null && nameValue != null && descValue != null && fileId != null && if (nameValue != null && descValue != null && fileId != null &&
name.statusForm.value == FormStatus.Valid && desc.statusForm.value == FormStatus.Valid name.statusForm.value == FormStatus.Valid && desc.statusForm.value == FormStatus.Valid
) { ) {
viewModelScope.launch { viewModelScope.launch {
HttpClient.post( HttpClient.post(
Api.buildUrl(AssociationApi.Register), Api.buildUrl(AssociationApi.Register),
SimpleCallback<Boolean>("注册社团", onSuccess = { HttpCallback<Boolean>("注册社团", onSuccess = {
Logger.i(it.message) Logger.i(it.message)
callback(it.message) callback(it.message)
name.clean() name.clean()
@ -110,13 +169,13 @@ class RegAssociationViewModel(application: Application) : AndroidViewModel(appli
_picture.postValue(null) _picture.postValue(null)
}, onFail = { }, onFail = {
Logger.e(it) Logger.e(it)
}, object : TypeToken<ApiResponse<Boolean>>() {}.type), }, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type),
jsonParam = jsonParam =
RegAssociationVo( AssociationRegVo(
name = nameValue, name = nameValue,
desc = descValue, desc = descValue,
token = token, fileId = fileId,
fileId = fileId id = checkInfo.value?.id
) )
) )
} }

@ -31,6 +31,8 @@ import com.gyf.csams.activity.ui.ApplyActActivity
import com.gyf.csams.association.model.* import com.gyf.csams.association.model.*
import com.gyf.csams.uikit.* import com.gyf.csams.uikit.*
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
import com.gyf.lib.util.HistoryActVo
import com.gyf.lib.util.MemberVo
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.randomChinese
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
@ -44,7 +46,7 @@ class AssociationActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
TestBody { nav, scaffoldState -> NavBody { nav, scaffoldState ->
val context = LocalContext.current as AssociationActivity val context = LocalContext.current as AssociationActivity
val model: AssociationViewModel = viewModel() val model: AssociationViewModel = viewModel()
val currentMenuName: AssociationMenu by model.currentMenu.observeAsState( val currentMenuName: AssociationMenu by model.currentMenu.observeAsState(

@ -19,7 +19,9 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.association.model.* import com.gyf.csams.association.model.ANSWER_SIZE
import com.gyf.csams.association.model.ExamActivityType
import com.gyf.csams.association.model.ExamViewModel
import com.gyf.csams.uikit.Background import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage import com.gyf.csams.uikit.BackgroundImage
import com.gyf.lib.uikit.BaseTextField import com.gyf.lib.uikit.BaseTextField
@ -27,6 +29,9 @@ import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.uikit.ScaffoldModel import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.ChoiceQuestionVo
import com.gyf.lib.util.Exam
import com.gyf.lib.util.OpenQuestionsVo
/** /**

@ -24,6 +24,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@ -33,8 +36,11 @@ import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage import com.gyf.csams.uikit.BackgroundImage
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
import com.gyf.lib.util.BottomButton import com.gyf.lib.util.BottomButton
import com.gyf.lib.util.CheckStatus
import com.gyf.lib.util.ImageUtil import com.gyf.lib.util.ImageUtil
import com.gyf.lib.util.format
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import java.util.*
/** /**
@ -57,6 +63,7 @@ class RegAssociationActivity : ComponentActivity() {
.weight(0.1F) .weight(0.1F)
) )
Title() Title()
Tip()
Name() Name()
Desc( Desc(
modifier = Modifier modifier = Modifier
@ -76,11 +83,13 @@ class RegAssociationActivity : ComponentActivity() {
val name by model.name.statusForm.observeAsState() val name by model.name.statusForm.observeAsState()
val desc by model.name.statusForm.observeAsState() val desc by model.name.statusForm.observeAsState()
val fileId by model.fileId.observeAsState() val fileId by model.fileId.observeAsState()
val checkInfo by model.checkInfo.observeAsState()
BottomButton( BottomButton(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
enabled = name == FormStatus.Valid && desc == FormStatus.Valid && fileId != null, enabled = name == FormStatus.Valid && desc == FormStatus.Valid && fileId != null,
confirmDesc = R.string.reg_btn confirmDesc = if (checkInfo != null) R.string.reg_again_btn else R.string.reg_btn,
) {
) {
model.register { model.register {
scaffoldModel.update(message = it, actionLabel = "返回") { scaffoldModel.update(message = it, actionLabel = "返回") {
finish() finish()
@ -88,10 +97,50 @@ class RegAssociationActivity : ComponentActivity() {
} }
} }
Spacer(modifier = Modifier.weight(0.05F)) Spacer(modifier = Modifier.weight(0.05F))
}
}
}
}
@Composable
private fun Tip(model: RegAssociationViewModel = viewModel()) {
val checkInfo by model.checkInfo.observeAsState()
checkInfo?.let {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
when (val status = it.checkStatus) {
CheckStatus.Finish -> Text(buildAnnotatedString {
withStyle(style = MaterialTheme.typography.h5.toParagraphStyle()) {
append("您于${Date(it.applyTime).format()}提交的${model.frameDesc}审核不通过原因如下:\n")
}
withStyle(
style = MaterialTheme.typography.h6.copy(color = MaterialTheme.colors.error)
.toParagraphStyle()
) {
append("初审意见:${it.firstCause}\n")
it.lastCause?.let {
append("复审意见:${it}")
}
}
})
else -> {
Text(
text = status.desc, style =
MaterialTheme.typography.h5.copy(color = MaterialTheme.colors.primary)
)
} }
} }
}
} }
}
@Preview
@Composable
private fun TestPreview9() {
Text(text = "123", color = MaterialTheme.colors.error)
} }
@ -187,30 +236,36 @@ class RegAssociationActivity : ComponentActivity() {
bitmap = it, contentDescription = null bitmap = it, contentDescription = null
) )
} }
Column( val checkInfo by model.checkInfo.observeAsState()
modifier = Modifier.fillMaxHeight(), if (checkInfo == null || checkInfo?.checkStatus == CheckStatus.Finish) {
verticalArrangement = Arrangement.SpaceEvenly Column(
) { modifier = Modifier.fillMaxHeight(),
IconButton(onClick = { verticalArrangement = Arrangement.SpaceEvenly
loadPicture() ) {
}) { IconButton(onClick = {
Image( loadPicture()
painter = painterResource(id = R.drawable.ic_exchange_rate), }) {
contentDescription = null Image(
) painter = painterResource(id = R.drawable.ic_exchange_rate),
} contentDescription = null
IconButton(onClick = { )
scaffoldModel.update("确认上传此图片?", actionLabel = "确定") { }
Logger.i("开始上传") IconButton(onClick = {
model.uploadPhoto { scaffoldModel.update("确认上传此图片?", actionLabel = "确定") {
scaffoldModel.update(message = it) Logger.i("开始上传")
model.uploadPhoto {
scaffoldModel.update(
message = it,
actionLabel = "关闭提示"
)
}
} }
}) {
Image(
painter = painterResource(id = R.drawable.ic_upload),
contentDescription = null
)
} }
}) {
Image(
painter = painterResource(id = R.drawable.ic_upload),
contentDescription = null
)
} }
} }
} }
@ -245,7 +300,11 @@ class RegAssociationActivity : ComponentActivity() {
*/ */
@Composable @Composable
private fun Name(model: RegAssociationViewModel = viewModel()) { private fun Name(model: RegAssociationViewModel = viewModel()) {
BaseTextField(form = model.name, singeLine = true, modifier = Modifier.fillMaxWidth()) val checkInfo by model.checkInfo.observeAsState()
BaseTextField(
form = model.name, singeLine = true, modifier = Modifier.fillMaxWidth(),
readOnly = checkInfo?.checkStatus != CheckStatus.Finish
)
} }
/** /**
@ -254,7 +313,11 @@ class RegAssociationActivity : ComponentActivity() {
*/ */
@Composable @Composable
private fun Desc(model: RegAssociationViewModel = viewModel(), modifier: Modifier) { private fun Desc(model: RegAssociationViewModel = viewModel(), modifier: Modifier) {
BaseTextField(form = model.desc, modifier = modifier) val checkInfo by model.checkInfo.observeAsState()
BaseTextField(
form = model.desc, modifier = modifier,
readOnly = checkInfo?.checkStatus != CheckStatus.Finish
)
} }
} }

@ -3,12 +3,9 @@ package com.gyf.csams.main.model
import android.app.Application import android.app.Application
import androidx.lifecycle.* import androidx.lifecycle.*
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.account.model.UserVo
import com.gyf.csams.uikit.AbstractComment import com.gyf.csams.uikit.AbstractComment
import com.gyf.csams.util.SimpleCallback
import com.gyf.lib.model.ScrollViewModel import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.uikit.FormStatus import com.gyf.lib.uikit.FormStatus
import com.gyf.lib.uikit.PersonInfoVo
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.uikit.ValidStringForm import com.gyf.lib.uikit.ValidStringForm
import com.gyf.lib.util.* import com.gyf.lib.util.*
@ -17,9 +14,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
data class LeaveMessageFormatVo(val message: String, val user: UserVo)
class NotificationViewModel(application: Application) : AndroidViewModel(application) { class NotificationViewModel(application: Application) : AndroidViewModel(application) {
private val _count = MutableLiveData<Long>() private val _count = MutableLiveData<Long>()
@ -35,24 +29,21 @@ class NotificationViewModel(application: Application) : AndroidViewModel(applica
*/ */
private fun count() { private fun count() {
viewModelScope.launch { viewModelScope.launch {
TokenManager.token?.let { HttpClient.post(
HttpClient.post( url = Api.buildUrl(NotificationApi.Count),
url = Api.buildUrl(NotificationApi.Count), HttpCallback<Long>(action = "未读通知计数", onSuccess = {
SimpleCallback<Long>(action = "未读通知计数", onSuccess = { it.body?.let {
it.body?.let { _count.postValue(it)
_count.postValue(it) }
} Logger.i(it.message)
Logger.i(it.message) }, onFail = {
}, onFail = { Logger.e(it)
Logger.e(it) }, typeToken = object : TypeToken<ApiResponse<Long>>() {}.type),
}, type = object : TypeToken<ApiResponse<Long>>() {}.type), jsonParam = NotificationDto(
jsonParam = NotificationDto( receiverId = TokenManager.getToken().id,
receiverId = it.id, receiverClient = ClientType.Foreground,
receiverClient = ClientType.Foreground,
token = it
)
) )
} )
} }
} }
} }
@ -87,32 +78,25 @@ class MarqueeViewModel : AbstractComment() {
* @param callback * @param callback
*/ */
override fun send(callback: (message: String) -> Unit) { override fun send(callback: (message: String) -> Unit) {
TokenManager.token.let { viewModelScope.launch {
if (it != null) { HttpClient.post(url = Api.buildUrl(MainApi.LeaveMessage),
viewModelScope.launch { HttpCallback<Boolean>(
HttpClient.post(url = Api.buildUrl(MainApi.LeaveMessage), action = "发送留言", onSuccess = {
SimpleCallback<Boolean>( if (it.body == true) {
action = "发送留言", onSuccess = { callback("留言发送成功")
if (it.body == true) { newContent.clean()
callback("留言发送成功") loadMessage(callback)
newContent.clean() } else {
loadMessage(callback) callback("留言发送失败")
} else { }
callback("留言发送失败") }, onFail = {
} callback("留言发送失败")
}, onFail = { }, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type
callback("留言发送失败") ),
}, type = object : TypeToken<ApiResponse<Boolean>>() {}.type jsonParam = LeaveMessageVo(
), message = newContent.formValue.value ?: ""
jsonParam = LeaveMessageVo( )
message = newContent.formValue.value ?: "", )
token = it
)
)
}
} else {
callback(UNKNOW_ERROR)
}
} }
} }
@ -121,16 +105,22 @@ class MarqueeViewModel : AbstractComment() {
viewModelScope.launch { viewModelScope.launch {
HttpClient.post( HttpClient.post(
Api.buildUrl(MainApi.GetMessage), Api.buildUrl(MainApi.GetMessage),
SimpleCallback<List<LeaveMessageFormatVo>>(action = "获取留言", onSuccess = { HttpCallback<List<LeaveMessageFormatVo>>(
action = "获取留言",
onSuccess = {
// callback(it.message) // callback(it.message)
Logger.i(it.message) Logger.i(it.message)
it.body?.let { it.body?.let {
_marqueeTexts.postValue(it) _marqueeTexts.postValue(it)
} }
}, onFail = { },
Logger.e(it) onFail = {
callback("留言获取失败") Logger.e(it)
}, type = object : TypeToken<ApiResponse<List<LeaveMessageFormatVo>>>() {}.type), callback("留言获取失败")
},
typeToken = object :
TypeToken<ApiResponse<List<LeaveMessageFormatVo>>>() {}.type
),
jsonParam = OnlyToken(clientType = ClientType.Foreground) jsonParam = OnlyToken(clientType = ClientType.Foreground)
) )
} }
@ -153,67 +143,47 @@ class MarqueeViewModel : AbstractComment() {
} }
/**
* 社团
*
* @property name 社团名称
*/
data class AssociationListVo(val name: String)
data class LeaveMessageVo(val message: String, val token: Token)
/** /**
* 社团列表 * 社团列表
* *
*/ */
class ListViewModel(application: Application) : ScrollViewModel<AssociationListVo>(application) { class ListViewModel(application: Application) : ScrollViewModel<AssociationVo>(application) {
val name = StringForm(formDesc = "社团名称", textLength = 5) val name = StringForm(formDesc = "社团名称", textLength = 5)
val desc = StringForm(formDesc = "社团简介", textLength = 10) val desc = StringForm(formDesc = "社团简介", textLength = 10)
//社团列表加载数量
val associationListSize = 10
//社团列表
private val _associationList = MutableLiveData<MutableList<AssociationListVo>>(mutableListOf())
val associationListVo: LiveData<MutableList<AssociationListVo>> = _associationList
val searchDesc = "搜索" val searchDesc = "搜索"
/**
* TODO 社团检索
*
* @param callback
*/
fun search(callback: (value: String) -> Unit) {
Logger.i("搜索条件[社团名称:${name.formValue.value},社团简介:${desc.formValue.value}]")
callback(NOT_IMPL_TIP)
}
override val initSize: Int = 10 override val initSize: Int = 10
init { init {
load() load {}
} }
/** /**
* 加载社团列表 * 加载社团列表
* *
*/ */
override fun load() { override fun load(callback: (message: String) -> Unit) {
viewModelScope.launch { viewModelScope.launch {
_associationList.value?.apply { HttpClient.post(
repeat( Api.buildUrl(AssociationApi.List),
10 HttpCallback<MutableList<AssociationVo>>(
) { action = "加载社团列表",
add(AssociationListVo(name = "社团${_associationList.value?.size}")) onSuccess = {
} it.body?.let {
_data.postValue(it)
} }
Logger.i("初始化社团列表size=${_associationList.value?.size}") callback(it.message)
},
typeToken = object :
TypeToken<ApiResponse<MutableList<AssociationVo>>>() {}.type
),
jsonParam = SearchAssociationVo(
name = name.formValue.value ?: "",
desc = desc.formValue.value ?: ""
)
)
} }
} }
@ -222,41 +192,10 @@ class ListViewModel(application: Application) : ScrollViewModel<AssociationListV
* *
*/ */
override fun loadMore(callback: (message: String) -> Unit) { override fun loadMore(callback: (message: String) -> Unit) {
viewModelScope.launch { TODO(NOT_IMPL_TIP)
_associationList.value?.apply {
val list = mutableListOf<AssociationListVo>()
list.addAll(this)
list.apply {
repeat(10) {
add(AssociationListVo(name = "社团${size}"))
}
}
Logger.i("t.size=${size}")
_associationList.postValue(list)
Logger.i("加载更多社团size=${_associationList.value?.size}")
callback("成功加载更多社团")
}
}
} }
} }
/**
* 我的信息
*
* @property studentId 学号
* @property name 姓名
* @property duty 职务
* @property headImg 头像
* @property desc 个人简介
*/
data class InfoVo(
val studentId: String,
override val name: String,
override val duty: String,
override val headImg: String,
override val desc: String
) : PersonInfoVo()
/** /**
* 个人中心 * 个人中心
@ -268,8 +207,6 @@ class CenterViewModel : ViewModel() {
val myLikeActivity = "我点赞的社团活动" val myLikeActivity = "我点赞的社团活动"
val myCollectActivity = "我收藏的社团活动" val myCollectActivity = "我收藏的社团活动"
private val _info = MutableLiveData<InfoVo>()
val info: LiveData<InfoVo> = _info
init { init {
load() load()
@ -277,13 +214,7 @@ class CenterViewModel : ViewModel() {
private fun load() { private fun load() {
viewModelScope.launch { viewModelScope.launch {
_info.value = InfoVo( TODO()
studentId = randomNum(),
name = randomChinese(3),
duty = randomChinese(3),
headImg = "",
desc = randomChinese(10)
)
} }
} }

@ -33,8 +33,7 @@ import com.gyf.csams.message.ui.MessageActivity
import com.gyf.csams.uikit.* import com.gyf.csams.uikit.*
import com.gyf.lib.service.BaseActivity import com.gyf.lib.service.BaseActivity
import com.gyf.lib.uikit.* import com.gyf.lib.uikit.*
import com.gyf.lib.util.ClientType import com.gyf.lib.util.*
import com.gyf.lib.util.randomChinese
/** /**
@ -51,7 +50,7 @@ class MainActivity : BaseActivity() {
setContent { setContent {
val imageViewModel: ImageViewModel = viewModel() val imageViewModel: ImageViewModel = viewModel()
TestBody { nav, scaffoldState -> NavBody { nav, scaffoldState ->
NavHost(navController = nav, startDestination = MainMenu.Main.name) { NavHost(navController = nav, startDestination = MainMenu.Main.name) {
composable(MainMenu.Main.name) { composable(MainMenu.Main.name) {
Main(navController = nav) Main(navController = nav)
@ -94,16 +93,11 @@ class MainActivity : BaseActivity() {
mainMenu = MainMenu.Center, mainMenu = MainMenu.Center,
nav = navController nav = navController
) { ) {
val info by model.info.observeAsState() Profile(
modifier = Modifier
info?.let { .weight(0.3F)
Profile( .padding(10.dp)
modifier = Modifier )
.weight(0.3F)
.padding(10.dp),
personInfoVo = it
)
}
Column( Column(
@ -193,7 +187,11 @@ class MainActivity : BaseActivity() {
mainMenu = MainMenu.List, mainMenu = MainMenu.List,
nav = navController nav = navController
) { ) {
RegisterAssociation() val memberVo: AssociationMemberVo? =
(TokenManager.getOwnInfo() as? UserVo)?.associationMemberVo
if (memberVo == null) {
RegisterAssociation()
}
AssociationSearch() AssociationSearch()
AssociationListBody() AssociationListBody()
} }
@ -233,7 +231,7 @@ class MainActivity : BaseActivity() {
model: ListViewModel = viewModel(), model: ListViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel() scaffoldModel: ScaffoldModel = viewModel()
) { ) {
val associationListList: MutableList<AssociationListVo>? by model.associationListVo.observeAsState() val associationListList: MutableList<AssociationVo>? by model.data.observeAsState()
val listState = rememberLazyListState() val listState = rememberLazyListState()
LazyColumn(state = listState) { LazyColumn(state = listState) {
@ -267,13 +265,13 @@ class MainActivity : BaseActivity() {
} }
} }
if (listState.layoutInfo.totalItemsCount - listState.firstVisibleItemIndex == model.associationListSize / 2 - 1) { if (listState.layoutInfo.totalItemsCount - listState.firstVisibleItemIndex == model.initSize / 2 - 1) {
model.loadMore { scaffoldModel.update(message = it) } model.loadMore { scaffoldModel.update(message = it) }
} }
} }
@Composable @Composable
private fun Association(associationListVo: AssociationListVo) { private fun Association(associationListVo: AssociationVo) {
val context = LocalContext.current val context = LocalContext.current
Card(modifier = Modifier.clickable(onClick = { Card(modifier = Modifier.clickable(onClick = {
context.startActivity( context.startActivity(
@ -307,7 +305,6 @@ class MainActivity : BaseActivity() {
model: ListViewModel = viewModel(), model: ListViewModel = viewModel(),
scaffoldModel: ScaffoldModel = viewModel() scaffoldModel: ScaffoldModel = viewModel()
) { ) {
Card(modifier = Modifier.padding(horizontal = 50.dp, vertical = 10.dp)) { Card(modifier = Modifier.padding(horizontal = 50.dp, vertical = 10.dp)) {
Column { Column {
@ -329,7 +326,11 @@ class MainActivity : BaseActivity() {
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
OutlinedButton( OutlinedButton(
onClick = { model.search { scaffoldModel.update(message = it) } }, onClick = {
model.load {
scaffoldModel.update(message = it)
}
},
modifier = Modifier.width(100.dp) modifier = Modifier.width(100.dp)
) { ) {
Text(text = model.searchDesc) Text(text = model.searchDesc)

@ -0,0 +1,11 @@
package com.gyf.csams.message.model
import android.app.Application
import com.gyf.lib.model.SysMessageViewModel
import com.gyf.lib.util.ClientType
class ForegroundViewModel(application: Application) : SysMessageViewModel(application) {
override fun clientType(): ClientType {
return ClientType.Foreground
}
}

@ -18,12 +18,17 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.csams.message.model.* import com.gyf.csams.message.model.ForegroundViewModel
import com.gyf.csams.message.model.MessageType
import com.gyf.csams.uikit.Background import com.gyf.csams.uikit.Background
import com.gyf.csams.uikit.BackgroundImage import com.gyf.csams.uikit.BackgroundImage
import com.gyf.csams.uikit.TextTopAppBar import com.gyf.csams.uikit.TextTopAppBar
import com.gyf.lib.model.ActCheckContent
import com.gyf.lib.model.JoinContent
import com.gyf.lib.model.RenameContent
import com.gyf.lib.uikit.Body import com.gyf.lib.uikit.Body
import com.gyf.lib.uikit.MainColumnFrame import com.gyf.lib.uikit.MainColumnFrame
import com.gyf.lib.util.NotificationVo
import com.gyf.lib.util.format import com.gyf.lib.util.format
import java.util.* import java.util.*
@ -54,7 +59,7 @@ class SysMessageActivity : ComponentActivity() {
@Composable @Composable
private fun MessageList( private fun MessageList(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
model: SysMessageViewModel = viewModel() model: ForegroundViewModel = viewModel()
) { ) {
val listState = rememberLazyListState() val listState = rememberLazyListState()
val list by model.data.observeAsState() val list by model.data.observeAsState()

@ -23,7 +23,6 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -591,7 +590,6 @@ fun MainContent() {
.padding(32.dp), .padding(32.dp),
verticalArrangement = Arrangement.spacedBy(24.dp) verticalArrangement = Arrangement.spacedBy(24.dp)
) { ) {
var textState by remember { mutableStateOf(TextFieldValue()) }
val localFocusManager = LocalFocusManager.current val localFocusManager = LocalFocusManager.current
val focusRequester = FocusRequester() val focusRequester = FocusRequester()

@ -8,11 +8,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.R import com.gyf.csams.R
import com.gyf.csams.util.SimpleCallback import com.gyf.lib.util.*
import com.gyf.lib.util.Api
import com.gyf.lib.util.HttpClient
import com.gyf.lib.util.ImageUtil
import com.gyf.lib.util.MainApi
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -57,7 +53,7 @@ class ImageViewModel(application: Application) : AndroidViewModel(application) {
HttpClient.get( HttpClient.get(
url = urlPath, url = urlPath,
SimpleCallback<List<String>>("获取轮播图", onSuccess = { HttpCallback<List<String>>("获取轮播图", onSuccess = {
_imageUrls.postValue(it.body) _imageUrls.postValue(it.body)
var index = 0 var index = 0
_imageUrls.value?.apply { _imageUrls.value?.apply {
@ -81,7 +77,7 @@ class ImageViewModel(application: Application) : AndroidViewModel(application) {
Logger.e("无法从接口地址:${urlPath}获取图片url列表") Logger.e("无法从接口地址:${urlPath}获取图片url列表")
_error.postValue(it) _error.postValue(it)
defaultLoad() defaultLoad()
}, type = object : TypeToken<List<String>>() {}.type) }, typeToken = object : TypeToken<ApiResponse<List<String>>>() {}.type)
) )
}.apply { }.apply {
start() start()

@ -2,8 +2,14 @@ package com.gyf.csams.util
import com.google.gson.* import com.google.gson.*
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.association.model.* import com.gyf.csams.association.model.ANSWER_SIZE
import com.gyf.csams.association.model.ANSWER_TEXT_LENGTH
import com.gyf.csams.association.model.QUESTION_TEXT_LENGTH
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.ChoiceQuestionVo
import com.gyf.lib.util.Exam
import com.gyf.lib.util.ExamType
import com.gyf.lib.util.OpenQuestionsVo
import java.lang.reflect.Type import java.lang.reflect.Type
class OpenQuestionsVoSerializer : JsonSerializer<OpenQuestionsVo> { class OpenQuestionsVoSerializer : JsonSerializer<OpenQuestionsVo> {

@ -2,8 +2,8 @@ package com.gyf.csams.util
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.gyf.csams.association.model.Exam
import com.gyf.lib.util.ApiResponse import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.Exam
import com.gyf.lib.util.HttpCallback import com.gyf.lib.util.HttpCallback
import java.lang.reflect.Type import java.lang.reflect.Type
@ -14,9 +14,9 @@ import java.lang.reflect.Type
* @property action * @property action
* @property onSuccess * @property onSuccess
* @property onFail * @property onFail
* @property type
*/ */
class SimpleCallback<T>( class ExamCallback<T>(
private val action: String, private val action: String,
private val onSuccess: (res: ApiResponse<T>) -> Unit, private val onSuccess: (res: ApiResponse<T>) -> Unit,
private val onFail: (error: String) -> Unit, private val onFail: (error: String) -> Unit,

@ -15,4 +15,5 @@
<string name="student_id_format">入学年份(四位)+班级代码(两位)+学生代码(两位)</string> <string name="student_id_format">入学年份(四位)+班级代码(两位)+学生代码(两位)</string>
<string name="welcome_start">同学您好\n</string> <string name="welcome_start">同学您好\n</string>
<string name="welcome_end">欢迎使用</string> <string name="welcome_end">欢迎使用</string>
<string name="reg_again_btn">再次申请</string>
</resources> </resources>

@ -15,4 +15,5 @@
<string name="student_id_format">入学年份(四位)+班级代码(两位)+学生代码(两位)</string> <string name="student_id_format">入学年份(四位)+班级代码(两位)+学生代码(两位)</string>
<string name="welcome_start">同学您好\n</string> <string name="welcome_start">同学您好\n</string>
<string name="welcome_end">欢迎使用</string> <string name="welcome_end">欢迎使用</string>
<string name="reg_again_btn">再次申请</string>
</resources> </resources>

@ -15,4 +15,5 @@
<string name="student_id_format">入学年份(四位)+班级代码(两位)+学生代码(两位)</string> <string name="student_id_format">入学年份(四位)+班级代码(两位)+学生代码(两位)</string>
<string name="welcome_start">同学您好\n</string> <string name="welcome_start">同学您好\n</string>
<string name="welcome_end">欢迎使用</string> <string name="welcome_end">欢迎使用</string>
<string name="reg_again_btn">再次申请</string>
</resources> </resources>

@ -2,6 +2,7 @@ package com.gyf.csams
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.ApiResponse import com.gyf.lib.util.ApiResponse
import com.gyf.lib.util.randomChinese import com.gyf.lib.util.randomChinese
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -39,9 +40,12 @@ class ExampleUnitTest {
repeat(100) { repeat(100) {
println(randomChinese()) println(randomChinese())
} }
}
// println(java.time.format.DateTimeFormatter.ISO_INSTANT @Test
// .format(java.time.Instant.ofEpochMilli(1532358895000))) fun testFind() {
val s = StringForm(formDesc = "", textLength = 3)
println("${s.formValue.value}")
} }
@Test @Test

@ -54,12 +54,13 @@ dependencies {
//生命周期组件版本 //生命周期组件版本
val lifecycle_version = "2.3.1" val lifecycle_version = "2.3.1"
//https://developer.android.com/topic/libraries/architecture/workmanager/basics?hl=zh-cn //https://developer.android.com/topic/libraries/architecture/workmanager/basics?hl=zh-cn
val work_version = "2.5.0"
val room_version = "2.3.0" val room_version = "2.3.0"
// Kotlin + coroutines val accompanist_version = "0.10.0"
api("androidx.work:work-runtime-ktx:$work_version") //https://github.com/google/accompanist
api("com.google.accompanist:accompanist-insets:$accompanist_version")
api("com.google.accompanist:accompanist-systemuicontroller:$accompanist_version")
api("com.google.accompanist:accompanist-coil:$accompanist_version")
/** /**
* 针对最新的平台功能和 API 调整应用,同时还支持旧设备。 * 针对最新的平台功能和 API 调整应用,同时还支持旧设备。
* https://developer.android.com/jetpack/androidx/releases/core * https://developer.android.com/jetpack/androidx/releases/core

@ -30,7 +30,7 @@ abstract class AbstractLoginViewModel(application: Application) : AndroidViewMod
/** /**
* 完成登录状态 * 完成登录状态
*/ */
private val _finishLogin = MutableLiveData<Boolean>() protected val _finishLogin = MutableLiveData<Boolean>()
val finishLogin: LiveData<Boolean> = _finishLogin val finishLogin: LiveData<Boolean> = _finishLogin
abstract fun checkForm(): Boolean abstract fun checkForm(): Boolean
@ -55,9 +55,9 @@ abstract class AbstractLoginViewModel(application: Application) : AndroidViewMod
val id = "${id.formValue.value}" val id = "${id.formValue.value}"
val password = "${password.formValue.value}" val password = "${password.formValue.value}"
Logger.i("使用账号:$id,密码:$password 进行登录") Logger.i("使用账号:$id,密码:$password 进行登录")
HttpClient.post(
url, val call = when (clientType) {
HttpCallback<Token>( ClientType.Background -> HttpCallback<ManagerVo>(
action = loginDesc, action = loginDesc,
onSuccess = { onSuccess = {
Logger.i(it.message) Logger.i(it.message)
@ -66,16 +66,40 @@ abstract class AbstractLoginViewModel(application: Application) : AndroidViewMod
it.body?.let { it.body?.let {
val db = AppDatabase.getInstance(context) val db = AppDatabase.getInstance(context)
viewModelScope.launch { viewModelScope.launch {
TokenManager.token = it TokenManager.init(it)
db?.tokenDao()?.save(token = it) db?.tokenDao()?.save(token = it.token)
}.invokeOnCompletion { }.invokeOnCompletion {
_finishLogin.postValue(true) _finishLogin.postValue(true)
} }
} }
}, },
onFail = { callback(it) }, onFail = { callback(it) },
type = object : TypeToken<ApiResponse<Token>>() {}.type typeToken = object : TypeToken<ApiResponse<ManagerVo>>() {}.type
), )
ClientType.Foreground -> HttpCallback<UserVo>(
action = loginDesc,
onSuccess = {
Logger.i(it.message)
callback(it.message)
val context = getApplication<Application>().applicationContext
it.body?.let {
val db = AppDatabase.getInstance(context)
viewModelScope.launch {
TokenManager.init(it)
db?.tokenDao()?.save(token = it.token)
}.invokeOnCompletion {
_finishLogin.postValue(true)
}
}
},
onFail = { callback(it) },
typeToken = object : TypeToken<ApiResponse<UserVo>>() {}.type
)
}
HttpClient.post(
url,
call,
jsonParam = loginParam() jsonParam = loginParam()
) )
} else { } else {
@ -91,8 +115,7 @@ abstract class AbstractLoginViewModel(application: Application) : AndroidViewMod
* @param callback * @param callback
*/ */
fun logout(context: Activity, callback: (message: String) -> Unit) { fun logout(context: Activity, callback: (message: String) -> Unit) {
TokenManager.token?.let {
Logger.i("帐号${it.id}将要退出登录")
viewModelScope.launch { viewModelScope.launch {
HttpClient.post( HttpClient.post(
Api.buildUrl(AccountApi.Logout), Api.buildUrl(AccountApi.Logout),
@ -112,16 +135,16 @@ abstract class AbstractLoginViewModel(application: Application) : AndroidViewMod
) )
) )
} }
TokenManager.token = null TokenManager.clear()
} }
} }
callback(it.message) callback(it.message)
}, onFail = { callback("退出登陆失败") }, },
type = object : TypeToken<ApiResponse<Boolean>>() {}.type onFail = { callback("退出登陆失败") },
typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type
), ),
jsonParam = OnlyToken(token = it, clientType = clientType) jsonParam = OnlyToken(clientType = clientType)
) )
}
} }
} }
} }

@ -5,6 +5,9 @@ import com.gyf.lib.R
import com.gyf.lib.uikit.StringForm import com.gyf.lib.uikit.StringForm
abstract class ApplyViewModel<T>(application: Application) : ScrollViewModel<T>(application) { abstract class ApplyViewModel<T>(application: Application) : ScrollViewModel<T>(application) {
val approverOrigin = val approveOrigin =
StringForm(formDesc = application.getString(R.string.approver_origin), textLength = 30) StringForm(
formDesc = application.getString(R.string.first_approver_origin),
textLength = 30
)
} }

@ -9,6 +9,7 @@ import com.google.gson.reflect.TypeToken
import com.gyf.lib.util.* import com.gyf.lib.util.*
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.lang.reflect.Type
class InitViewModel(application: Application) : AndroidViewModel(application) { class InitViewModel(application: Application) : AndroidViewModel(application) {
/** /**
@ -17,7 +18,6 @@ class InitViewModel(application: Application) : AndroidViewModel(application) {
private val _isNetWorkWorking = MutableLiveData<Boolean>() private val _isNetWorkWorking = MutableLiveData<Boolean>()
val isNetWorkWorking: LiveData<Boolean> = _isNetWorkWorking val isNetWorkWorking: LiveData<Boolean> = _isNetWorkWorking
init { init {
checkServer() checkServer()
} }
@ -32,7 +32,7 @@ class InitViewModel(application: Application) : AndroidViewModel(application) {
}, onFail = { }, onFail = {
Logger.e(it) Logger.e(it)
_isNetWorkWorking.postValue(false) _isNetWorkWorking.postValue(false)
}, type = object : TypeToken<ApiResponse<Boolean>>() {}.type) }, typeToken = object : TypeToken<ApiResponse<Boolean>>() {}.type)
) )
} }
@ -41,7 +41,10 @@ class InitViewModel(application: Application) : AndroidViewModel(application) {
/** /**
* 查询本地是否有且只有一个用户token如果有则自动登录 * 查询本地是否有且只有一个用户token如果有则自动登录
*/ */
fun hasOnlyUserToken(onSuccess: () -> Unit, onFail: () -> Unit, api: AccountApi) { fun <T : OwnInfoVo> hasOnlyUserToken(
onSuccess: () -> Unit, onFail: () -> Unit, api: AccountApi,
typeToken: Type
) {
viewModelScope.launch { viewModelScope.launch {
val context = getApplication<Application>() val context = getApplication<Application>()
val db = AppDatabase.getInstance(context) val db = AppDatabase.getInstance(context)
@ -53,14 +56,12 @@ class InitViewModel(application: Application) : AndroidViewModel(application) {
Logger.i("${action}api=$url") Logger.i("${action}api=$url")
HttpClient.post( HttpClient.post(
url, url,
HttpCallback<Boolean>( HttpCallback<T>(
action = action, action = action,
onSuccess = { it -> onSuccess = { it ->
it.body?.let { it.body?.let {
Logger.i("token校验结果:${it}") Logger.i("token校验结果:${it}")
if (it) { TokenManager.init(it)
TokenManager.token = currentToken
}
onSuccess() onSuccess()
} }
@ -68,11 +69,10 @@ class InitViewModel(application: Application) : AndroidViewModel(application) {
onFail = { onFail = {
Logger.e(it) Logger.e(it)
onFail() onFail()
}, }, typeToken = typeToken),
type = object : TypeToken<ApiResponse<Boolean>>() {}.type
),
jsonParam = currentToken jsonParam = currentToken
) )
} else if (tokenList != null && tokenList.size > 1) { } else if (tokenList != null && tokenList.size > 1) {
//TODO 实现切换历史登录帐号 //TODO 实现切换历史登录帐号

@ -11,8 +11,13 @@ abstract class ScrollViewModel<T>(application: Application) : AndroidViewModel(a
abstract val initSize: Int abstract val initSize: Int
//加载列表 //加载列表
abstract fun load() @Deprecated("")
open fun load() {
}
open fun load(callback: (message: String) -> Unit) {}
//加载更多数据 //加载更多数据
abstract fun loadMore(callback: (message: String) -> Unit) abstract fun loadMore(callback: (message: String) -> Unit)

@ -1,12 +1,10 @@
package com.gyf.csams.message.model package com.gyf.lib.model
import android.app.Application import android.app.Application
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.gyf.csams.util.SimpleCallback
import com.gyf.lib.model.ScrollViewModel
import com.gyf.lib.util.* import com.gyf.lib.util.*
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -19,8 +17,6 @@ enum class SystemType(val desc: String) {
} }
data class NotificationVo(val title: String, val content: String, val id: Int, val createTime: Long)
/** /**
* 通知内容 * 通知内容
* *
@ -82,7 +78,8 @@ data class RenameContent(
* 系统通知数据状态管理 * 系统通知数据状态管理
* *
*/ */
class SysMessageViewModel(application: Application) : ScrollViewModel<NotificationVo>(application) { abstract class SysMessageViewModel(application: Application) :
ScrollViewModel<NotificationVo>(application) {
val title = "系统通知" val title = "系统通知"
override val initSize: Int = 10 override val initSize: Int = 10
@ -90,43 +87,43 @@ class SysMessageViewModel(application: Application) : ScrollViewModel<Notificati
private val _currentPage = MutableLiveData<Long>() private val _currentPage = MutableLiveData<Long>()
val currentPage: LiveData<Long> = _currentPage val currentPage: LiveData<Long> = _currentPage
abstract fun clientType(): ClientType
init { init {
load() load()
} }
/** /**
*加载通知列表 *加载通知列表
* *
*/ */
override fun load() { final override fun load() {
viewModelScope.launch { viewModelScope.launch {
TokenManager.token?.let { it -> HttpClient.post(
HttpClient.post( Api.buildUrl(NotificationApi.List),
Api.buildUrl(NotificationApi.List), HttpCallback<MutableList<NotificationVo>>(
SimpleCallback<MutableList<NotificationVo>>( action = "获取通知列表",
action = "获取通知列表", onSuccess = {
onSuccess = { it.body?.let {
it.body?.let { _data.postValue(it)
_data.postValue(it) }
} Logger.i(it.message)
Logger.i(it.message) },
}, onFail = {
onFail = { Logger.e(it)
Logger.e(it) },
}, typeToken = object :
type = object : TypeToken<ApiResponse<MutableList<NotificationVo>>>() {}.type
TypeToken<ApiResponse<MutableList<NotificationVo>>>() {}.type ),
), jsonParam = NotificationDto(
jsonParam = NotificationDto( receiverId = TokenManager.getToken().id,
receiverId = it.id, receiverClient = clientType(), page = PageDto(
receiverClient = ClientType.Foreground, currentPage = _currentPage.value ?: 1,
token = it, page = PageDto( pageSize = initSize
currentPage = _currentPage.value ?: 1,
pageSize = initSize
)
) )
) )
} )
} }
} }

@ -32,13 +32,11 @@ class MessageService : JobIntentService() {
} }
override fun onHandleWork(intent: Intent) { override fun onHandleWork(intent: Intent) {
TokenManager.token?.let { it ->
HttpClient.postAsync<List<NotificationVo>>( HttpClient.postAsync<List<NotificationVo>>(
url = Api.buildUrl(NotificationApi.Pull), url = Api.buildUrl(NotificationApi.Pull),
jsonParam = NotificationDto( jsonParam = NotificationDto(
receiverId = it.id, receiverId = TokenManager.getToken().id,
receiverClient = clientType, receiverClient = clientType
token = it
), ),
type = object : TypeToken<ApiResponse<List<NotificationVo>>>() {}.type type = object : TypeToken<ApiResponse<List<NotificationVo>>>() {}.type
)?.let { it1 -> )?.let { it1 ->
@ -57,7 +55,6 @@ class MessageService : JobIntentService() {
notify(it.id, builder.build()) notify(it.id, builder.build())
} }
} }
}
} }
} }
} }

@ -12,12 +12,14 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.gyf.lib.BuildConfig import com.gyf.lib.BuildConfig
import com.gyf.lib.model.InitViewModel import com.gyf.lib.model.InitViewModel
import com.gyf.lib.util.AccountApi import com.gyf.lib.util.AccountApi
import com.gyf.lib.util.OwnInfoVo
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.lang.reflect.Type
abstract class AbstractInitActivity : ComponentActivity() { abstract class AbstractInitActivity<T : OwnInfoVo> : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -49,13 +51,15 @@ abstract class AbstractInitActivity : ComponentActivity() {
abstract val api: AccountApi abstract val api: AccountApi
abstract val typeToken: Type
private fun init(initViewModel: InitViewModel) { private fun init(initViewModel: InitViewModel) {
//后台检查token //后台检查token
initViewModel.hasOnlyUserToken(onSuccess = { initViewModel.hasOnlyUserToken<T>(onSuccess = {
startActivity(Intent(this, main)) startActivity(Intent(this, main))
}, onFail = { }, onFail = {
startActivity(Intent(this, login)) startActivity(Intent(this, login))
}, api = api) }, api = api, typeToken = typeToken)
GlobalScope.launch { GlobalScope.launch {
delay(1000) delay(1000)
finish() finish()

@ -8,6 +8,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.google.accompanist.insets.ExperimentalAnimatedInsets
import com.google.accompanist.insets.ProvideWindowInsets
import com.gyf.lib.uikit.theme.CSAMSTheme import com.gyf.lib.uikit.theme.CSAMSTheme
@ -157,9 +159,24 @@ fun Body(content: @Composable () -> Unit) {
} }
} }
@ExperimentalAnimatedInsets
@Composable
fun ImeBody(content: @Composable () -> Unit) {
CSAMSTheme {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Surface(color = MaterialTheme.colors.background) {
val scaffoldState = rememberScaffoldState()
Scaffold(scaffoldState = scaffoldState) {
content()
ShowSnackbar(scaffoldState = scaffoldState)
}
}
}
}
}
@Composable @Composable
fun TestBody(content: @Composable (nav: NavHostController, scaffoldState: ScaffoldState) -> Unit) { fun NavBody(content: @Composable (nav: NavHostController, scaffoldState: ScaffoldState) -> Unit) {
CSAMSTheme { CSAMSTheme {
Surface(color = MaterialTheme.colors.background) { Surface(color = MaterialTheme.colors.background) {
val navController = rememberNavController() val navController = rememberNavController()
@ -170,4 +187,5 @@ fun TestBody(content: @Composable (nav: NavHostController, scaffoldState: Scaffo
} }
} }
} }
} }

@ -12,21 +12,23 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.gyf.lib.util.ImageUtil import com.gyf.lib.util.*
abstract class PersonInfoVo {
abstract val name: String
abstract val duty: String
abstract val headImg: String
abstract val desc: String
}
@Composable @Composable
fun Profile( fun Profile(
modifier: Modifier, modifier: Modifier,
personInfoVo: PersonInfoVo, personInfoVo: PersonInfoVo = TokenManager.getOwnInfo(),
duty: @Composable () -> Unit = { Text(text = personInfoVo.duty) } duty: @Composable () -> Unit = {
Text(
text = when (personInfoVo) {
is UserVo -> personInfoVo.manager?.duty?.desc ?: "----"
is ManagerVo -> personInfoVo.duty.desc
is ManagerInfoVo -> personInfoVo.duty.desc
else -> throw IllegalArgumentException("个人信息类型错误:${personInfoVo}")
}
)
}
) { ) {
var headImg: ImageBitmap? by remember { var headImg: ImageBitmap? by remember {
mutableStateOf(null) mutableStateOf(null)

@ -33,7 +33,6 @@ enum class AccountApi(val path: String) : UrlPath {
//后台登陆 //后台登陆
BackgroundLogin("/login/${ClientType.Background.name.toLowerCase(Locale.ROOT)}"), BackgroundLogin("/login/${ClientType.Background.name.toLowerCase(Locale.ROOT)}"),
//前台令牌校验 //前台令牌校验
ForegroundToken("${ForegroundLogin.path}/token"), ForegroundToken("${ForegroundLogin.path}/token"),
@ -74,8 +73,12 @@ enum class MainApi(val path: String) : UrlPath {
*/ */
enum class AssociationApi(val path: String) : UrlPath { enum class AssociationApi(val path: String) : UrlPath {
Logo("/uploadLogo"), Logo("/uploadLogo"),
Register("/register"); Register("/register"),
Accept("/accept"),
List("/list"),
Check("/check"),
Audit("/audit"),
Read("/read");
override fun build(): String { override fun build(): String {
return "/api/association${this.path}" return "/api/association${this.path}"

@ -22,7 +22,8 @@ fun BottomButton(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true, enabled: Boolean = true,
@StringRes confirmDesc: Int = R.string.confirm_btn, @StringRes confirmDesc: Int = R.string.confirm_btn,
@StringRes backDesc: Int = R.string.back_btn, @StringRes backDesc: Int? = R.string.back_btn,
onBack: (() -> Unit)? = null,
onConfirm: () -> Unit onConfirm: () -> Unit
) { ) {
val context = LocalContext.current as Activity val context = LocalContext.current as Activity
@ -34,11 +35,16 @@ fun BottomButton(
) { ) {
Text(text = stringResource(id = confirmDesc)) Text(text = stringResource(id = confirmDesc))
} }
Spacer(modifier = Modifier.width(10.dp))
OutlinedButton(onClick = { if (backDesc != null) {
context.onBackPressed() Spacer(modifier = Modifier.width(10.dp))
}, modifier = Modifier.background(color = MaterialTheme.colors.secondary)) { OutlinedButton(onClick = {
Text(text = stringResource(id = backDesc)) if (onBack == null) context.onBackPressed() else onBack()
}, modifier = Modifier.background(color = MaterialTheme.colors.secondary)) {
Text(text = stringResource(id = backDesc))
}
} }
} }
} }

@ -1,6 +1,7 @@
package com.gyf.lib.util package com.gyf.lib.util
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import okhttp3.* import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
@ -116,8 +117,9 @@ object HttpClient {
* @param url * @param url
* @param callback * @param callback
*/ */
fun uploadFile(url: String, callback: Callback, token: Token, vararg fileList: File) { fun uploadFile(url: String, callback: Callback, vararg fileList: File) {
Logger.i("request url=$url") Logger.i("request url=$url")
val token = TokenManager.getToken()
val body = MultipartBody.Builder() val body = MultipartBody.Builder()
.addFormDataPart("id", token.id.toString()) .addFormDataPart("id", token.id.toString())
.addFormDataPart("token", token.token) .addFormDataPart("token", token.token)
@ -155,9 +157,9 @@ interface GsonBuilderInterface {
open class HttpCallback<T>( open class HttpCallback<T>(
private val action: String, private val action: String,
private val onSuccess: (res: ApiResponse<T>) -> Unit, private val onSuccess: (res: ApiResponse<T>) -> Unit = { Logger.i(it.message) },
private val onFail: (error: String) -> Unit, private val onFail: (error: String) -> Unit = { Logger.e(it) },
private val type: Type private val typeToken: Type
) : Callback, GsonBuilderInterface { ) : Callback, GsonBuilderInterface {
override val gson: Gson = Gson() override val gson: Gson = Gson()
@ -178,11 +180,18 @@ open class HttpCallback<T>(
val body = response.body val body = response.body
if (body != null && body.contentType()?.subtype == "json") { if (body != null && body.contentType()?.subtype == "json") {
val jsonRes = body.string() val jsonRes = body.string()
Logger.json(jsonRes) Logger.i(jsonRes)
val res: ApiResponse<T> = gson.fromJson(jsonRes, type) try {
Logger.i("json解析成功:$res") val res: ApiResponse<T> = gson.fromJson(jsonRes, typeToken)
onSuccess(res)
} catch (e: JsonSyntaxException) {
Logger.e(e, "json反序列化成${typeToken}失败")
onFail("json反序列化成${typeToken}失败")
} catch (e: Exception) {
Logger.e(e, "发生未知错误")
onFail("发生未知错误")
}
onSuccess(res)
} else { } else {
onFail("${action}失败,请联系管理员") onFail("${action}失败,请联系管理员")
Logger.e("无法解析${action}请求响应数据:,响应码:${response.code},${response.body}") Logger.e("无法解析${action}请求响应数据:,响应码:${response.code},${response.body}")

@ -17,16 +17,11 @@ data class Token(
) )
abstract class ClientBaseVo { abstract class ClientBaseVo {
abstract val token: Token val token: Token = TokenManager.getToken()
abstract val clientType: ClientType abstract val clientType: ClientType
} }
abstract class BaseToken {
abstract val token: Token
}
data class OnlyToken( data class OnlyToken(
override val token: Token = TokenManager.token ?: throw IllegalArgumentException("无法获取token"),
override val clientType: ClientType override val clientType: ClientType
) : ClientBaseVo() ) : ClientBaseVo()
@ -46,8 +41,39 @@ interface TokenDao {
} }
object TokenManager { object TokenManager {
var token: Token? = null private var ownInfo: OwnInfoVo? = null
private lateinit var personInfo: PersonInfoVo
fun init(ownInfo: OwnInfoVo) {
this.ownInfo = ownInfo
when (ownInfo) {
is ManagerVo -> this.personInfo = ManagerInfoVo(
duty = ownInfo.duty,
name = ownInfo.name,
headImg = ownInfo.headImg,
desc = ownInfo.desc
)
is UserVo -> this.personInfo =
UserInfoVo(name = ownInfo.name, headImg = ownInfo.headImg, desc = ownInfo.desc)
else -> throw IllegalArgumentException("token失败")
}
}
fun clear() {
ownInfo = null
}
fun getOwnInfo(): OwnInfoVo {
return ownInfo ?: throw IllegalArgumentException("token没有初始化,非法调用")
}
fun getPersonInfo(): PersonInfoVo {
return personInfo
}
fun getToken(): Token {
return ownInfo?.token ?: throw IllegalArgumentException("token没有初始化,非法调用")
}
} }

@ -0,0 +1,477 @@
package com.gyf.lib.util
import androidx.annotation.IntRange
import com.gyf.lib.uikit.StringForm
import java.util.*
/**
* 一般信息
*
*/
abstract class PersonInfoVo {
abstract val name: String
abstract val headImg: String?
abstract val desc: String
}
enum class Duty(val desc: String, val level: Int) {
Teacher("老师", 1),
PamphaBhusal("总部长", 2),
SecretaryOfTheMinister("秘书部部长", 3),
PropagandaDepartment("宣传部部长", 3),
LiaisonMinister("外联部部长", 3),
SecretaryDepartmentOfficer("秘书部干事", 4),
PublicityDepartmentOfficer("宣传部干事", 4),
LiaisonOfficer("外联部干事", 4);
/**
* 是否是部门部长
*
*/
fun isMinister(): Boolean {
return minister.contains(this)
}
fun isOfficer(): Boolean {
return officer.contains(this)
}
}
private val minister =
arrayOf(Duty.SecretaryOfTheMinister, Duty.LiaisonMinister, Duty.PropagandaDepartment)
private val officer =
arrayOf(Duty.SecretaryDepartmentOfficer, Duty.PublicityDepartmentOfficer, Duty.LiaisonOfficer)
/**
* 个人信息
*
*/
abstract class OwnInfoVo : PersonInfoVo() {
abstract val token: Token
}
data class ManagerInfoVo(
val duty: Duty,
override val name: String,
override val headImg: String?,
override val desc: String
) : PersonInfoVo()
data class UserInfoVo(
override val name: String,
override val headImg: String?,
override val desc: String
) : PersonInfoVo()
/**
* 管理员个人信息
*
* @property account 管理员账号
* @property name 姓名
* @property duty 职务
* @property headImg 头像
* @property desc 个人简介
*/
data class ManagerVo(
val account: String,
val duty: Duty,
override val token: Token,
override val name: String,
override val headImg: String?,
override val desc: String
) : OwnInfoVo()
/**
* 用户个人信息
*
* @property studentId 学号
* @property name 姓名
* @property headImg 头像
* @property desc 个人简介
*/
data class UserVo(
val studentId: String,
val manager: ManagerVo? = null,
override val token: Token,
override val name: String,
override val headImg: String?,
override val desc: String,
val associationMemberVo: AssociationMemberVo?
) : OwnInfoVo()
data class PageDto(val currentPage: Long, val pageSize: Int = 10)
data class NotificationDto(
val receiverId: Int,
val receiverClient: ClientType,
val page: PageDto? = null,
override val clientType: ClientType = receiverClient
) : ClientBaseVo()
data class UserRegVo(val studentId: String, val name: String)
/**
* 客户端类型
*
*/
enum class ClientType {
//前台
Foreground,
//后台
Background
}
data class NotificationVo(val title: String, val content: String, val id: Int, val createTime: Long)
/**
* 响应自动生成密码
*
* @property password
*/
data class UserPasswordVo(val password: String)
/**
* 用户登陆表单
*
* @property studentId 学号
* @property password 密码
* @property device 设备型号
*/
data class UserLoginVo(val studentId: String, val password: String, val device: String)
/**
*
* @property associationName 社团名字
* @property activityName 活动名
* @property activityTime 活动时间
* @property activityLocation 活动地点
* @property activityDesc 活动介绍
*/
data class ActivityDetailVo(
val associationName: String, val activityName: String,
val activityTime: Date, val activityLocation: String,
val activityDesc: String
)
data class ActivityMemberVo(val studentId: String, val name: String)
/**
* 活动成员
*
* @property organizer
* @property participant
*/
data class ActivityMembersVo(
val organizer: ActivityMemberVo,
val participant: MutableList<ActivityMemberVo>?
)
/**
* 图片
* @property name 文件名
* @property size 文件大小
* @property url 文件路径
* @property md5 文件hash
* @property createTime 文件创建时间
* @property studentId 文件上传人
*/
data class ActivityPhotoVo(
val name: String,
val size: Long,
val url: String,
val md5: String,
val createTime: Date,
val studentId: String
)
const val MAX_SCORE = 5L
data class ActivityVo(
val activityId: Long, val activityName: String, val association: String,
@IntRange(from = 1, to = MAX_SCORE) val score: Int, val activityTime: Date, val location: String
)
data class AllOfficerVo(
val secretariat: MutableList<ManagerInfoVo>,
val propaganda: MutableList<ManagerInfoVo>,
val publicRelationsDepartment: MutableList<ManagerInfoVo>
)
data class ApplyActVo(
val activityName: String, val activityTime: String,
val location: String, val desc: String,
val size: Int
)
data class LeaveMessageVo(
val message: String,
override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo()
/**
* 社团级别
*
*/
enum class AssociationLevel {
A,
B,
C,
D
}
/**
* 所属院系
*
*/
enum class AssociationFaculty(val desc: String, val range: kotlin.ranges.IntRange) {
ForeignLanguageDept("外语系", 0..0),
CivilEngineeringDept("土木工程", 1..10),
SEM("经理管理学院", 11..20),
MechanicalEngineeringDept("机械工程", 21..30),
TransportationDept("交通运输", 31..40),
ArchitectureAndArts("建筑与艺术", 41..50),
ElectricalDept("电气", 51..60),
MaterialsDept("材料", 61..70),
MessageDept("信息", 71..80),
MathematicsDept("数理", 81..90),
GraduateStudent("研究生", 91..99)
}
abstract class BaseAssociationVo {
abstract val id: Int
abstract val name: String
abstract val desc: String
abstract val logo: String
abstract val faculty: AssociationFaculty
abstract val level: AssociationLevel?
}
/**
* 社团列表
*
*/
class AssociationVo(
override val id: Int,
override val name: String,
override val desc: String,
override val logo: String,
override val faculty: AssociationFaculty,
override val level: AssociationLevel?
) : BaseAssociationVo()
//审核状态
enum class CheckStatus(val desc: String) {
WaitFirst("等待初审"),
AcceptFirst("初审受理"),
WaitLast("等待复审"),
AcceptLast("复审受理"),
Finish("审核完成")
}
data class AssociationMemberVo(
val association: AssociationVo,
val isHead: Boolean
)
//用户社团
data class AssociationCheckVo(
override val id: Int,
override val name: String,
override val desc: String,
override val logo: String,
override val faculty: AssociationFaculty,
override val level: AssociationLevel?,
val checkStatus: CheckStatus,
val applyTime: Long,
val firstCause: String,
val lastCause: String?,
val fileId: Int
) : BaseAssociationVo()
/**
* 搜索社团
*
* @property name
* @property desc
* @property clientType
*/
data class SearchAssociationVo(
val name: String, val desc: String,
override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo()
data class BBSVo(val studentId: String, val name: String, val createTime: Date, val content: String)
/**
* 题型
*
*/
enum class ExamType(val type: String) {
//选择题
CQ("选择题"),
//开放题
OQ("开放题")
}
abstract class Exam {
abstract val examType: ExamType
abstract val question: StringForm
}
/**
* 选择题
*
* @property examType 题型描述
* @property answers 答案
* @property rightAnswer 正确答案
* @property question 问题
*/
data class ChoiceQuestionVo(
override val examType: ExamType = ExamType.CQ,
val answers: List<StringForm>,
val rightAnswer: Int,
override val question: StringForm
) : Exam()
data class HistoryActVo(val name: String)
/**
* 开放题
*
* @property examType 题型描述
* @property question 问题
*/
data class OpenQuestionsVo(
override val examType: ExamType = ExamType.OQ, override val question: StringForm
) : Exam()
data class LeaveMessageFormatVo(val message: String, val user: UserInfoVo)
data class ManagerLoginVo(val account: String, val password: String, val device: String)
data class MemberVo(val name: String)
data class OngoingActVo(val name: String)
/**
* 活动质量汇报单
*
* @property applyName 申请人
* @property activityName 活动名称
* @property merit 优点
* @property defect 缺点
* @property score 星级评价
*/
data class QualityReportVo(
val applyName: String,
val activityName: String,
val merit: String,
val defect: String,
@IntRange(from = 1L, to = MAX_SCORE) val score: Int
)
/**
* 社团注册资料表单
*
* @property name
* @property desc
* @property fileId
*/
data class AssociationRegVo(
val id: Int?, val name: String, val desc: String, val fileId: Int,
override val clientType: ClientType = ClientType.Foreground
) : ClientBaseVo()
/**
* 社团注册审核记录
*
*/
data class DisposeRegInfoVo(
val name: String,
val desc: String,
val logo: String,
val log: AuditLoggingVo
)
/**
* 通用审核记录
*
* @property id
* @property user
* @property applyTime
* @property manager
* @property acceptTime
* @property cause
* @property result
* @property auditTime
* @property nextAudit
*/
data class AuditLoggingVo(
val id: Int, val user: UserInfoVo, val applyTime: Long, val manager: ManagerInfoVo?,
val acceptTime: Long?, val cause: String?, val result: Boolean?,
val auditTime: Long?, val nextAudit: AuditLoggingVo?
)
/**
* 社团注册资料受理
*
* @property regId
* @property clientType
*/
data class AcceptRegAssociation(
val regId: Int,
val isFirstAccept: Boolean,
override val clientType: ClientType = ClientType.Background
) : ClientBaseVo()
/**
* 社团注册资料审核
*
* @property regId
* @property result
* @property cause
* @property clientType
*/
data class CheckRegVo(
val regId: Int, val result: Boolean, val cause: String,
override val clientType: ClientType = ClientType.Background
) : ClientBaseVo()
/**
* 换名申请表
*
* @property studentId 学号
* @property oldName 社团原名
* @property newName 社团新名
* @property reason 申请理由
*/
data class RenameVo(
val studentId: String,
val oldName: String,
val newName: String,
val reason: String
)

@ -1,27 +0,0 @@
package com.gyf.lib.util
data class NotificationVo(val title: String, val content: String, val id: Int)
data class PageDto(val currentPage: Long, val pageSize: Int = 10)
data class NotificationDto(
val receiverId: Int,
val receiverClient: ClientType,
override val token: Token,
val page: PageDto? = null,
override val clientType: ClientType = receiverClient
) : ClientBaseVo()
/**
* 客户端类型
*
*/
enum class ClientType {
//前台
Foreground,
//后台
Background
}

@ -17,9 +17,17 @@
<string name="oldname">社团原名</string> <string name="oldname">社团原名</string>
<string name="newname">社团新名</string> <string name="newname">社团新名</string>
<string name="reason_for_application">申请理由</string> <string name="reason_for_application">申请理由</string>
<string name="approver">审批</string> <string name="first_approver">初审负责</string>
<string name="approver_origin">审核理由</string> <string name="first_approver_origin">初审意见</string>
<string name="not_impl_error">抱歉此功能尚未开放</string> <string name="not_impl_error">抱歉此功能尚未开放</string>
<string name="quality_report_title">活动质量汇报单</string> <string name="quality_report_title">活动质量汇报单</string>
<string name="login_btn">登录</string> <string name="login_btn">登录</string>
<string name="association_reg_title">社团注册资料</string>
<string name="association_desc">社团简介</string>
<string name="association_logo">社团logo</string>
<string name="association_name">社团名称</string>
<string name="approver_result">审核结果</string>
<string name="recheck_btn">复审</string>
<string name="audit_phases">审核阶段</string>
<string name="last_approver_origin">复审意见</string>
</resources> </resources>

@ -17,9 +17,17 @@
<string name="oldname">社团原名</string> <string name="oldname">社团原名</string>
<string name="newname">社团新名</string> <string name="newname">社团新名</string>
<string name="reason_for_application">申请理由</string> <string name="reason_for_application">申请理由</string>
<string name="approver">审批</string> <string name="first_approver">初审负责</string>
<string name="approver_origin">审核理由</string> <string name="first_approver_origin">初审意见</string>
<string name="not_impl_error">抱歉此功能尚未开放</string> <string name="not_impl_error">抱歉此功能尚未开放</string>
<string name="quality_report_title">活动质量汇报单</string> <string name="quality_report_title">活动质量汇报单</string>
<string name="login_btn">登录</string> <string name="login_btn">登录</string>
<string name="association_reg_title">社团注册资料</string>
<string name="association_desc">社团简介</string>
<string name="association_logo">社团logo</string>
<string name="association_name">社团名称</string>
<string name="approver_result">审核结果</string>
<string name="recheck_btn">复审</string>
<string name="audit_phases">审核阶段</string>
<string name="last_approver_origin">复审意见</string>
</resources> </resources>

@ -17,9 +17,17 @@
<string name="oldname">社团原名</string> <string name="oldname">社团原名</string>
<string name="newname">社团新名</string> <string name="newname">社团新名</string>
<string name="reason_for_application">申请理由</string> <string name="reason_for_application">申请理由</string>
<string name="approver">审批</string> <string name="first_approver">初审负责</string>
<string name="approver_origin">审核理由</string> <string name="first_approver_origin">初审意见</string>
<string name="not_impl_error">抱歉此功能尚未开放</string> <string name="not_impl_error">抱歉此功能尚未开放</string>
<string name="quality_report_title">活动质量汇报单</string> <string name="quality_report_title">活动质量汇报单</string>
<string name="login_btn">登录</string> <string name="login_btn">登录</string>
<string name="association_reg_title">社团注册资料</string>
<string name="association_desc">社团简介</string>
<string name="association_logo">社团logo</string>
<string name="association_name">社团名称</string>
<string name="approver_result">审核结果</string>
<string name="recheck_btn">复审</string>
<string name="audit_phases">审核阶段</string>
<string name="last_approver_origin">复审意见</string>
</resources> </resources>
Loading…
Cancel
Save