Kaynağa Gözat

add(study plan): add index viewmodel

zhaoyadi 1 yıl önce
ebeveyn
işleme
63bb5ac6f1
52 değiştirilmiş dosya ile 594 ekleme ve 116 silme
  1. 3 0
      .idea/gradle.xml
  2. 5 0
      built/convention/build.gradle.kts
  3. 17 0
      built/convention/src/main/kotlin/LifecycleConventionPlugin.kt
  4. 9 0
      core/event/build.gradle.kts
  5. 24 0
      core/event/src/androidTest/java/com/zaojiao/app/core/event/ExampleInstrumentedTest.kt
  6. 4 0
      core/event/src/main/AndroidManifest.xml
  7. 8 0
      core/event/src/main/java/com/zaojiao/app/core/event/Event.kt
  8. 41 0
      core/event/src/main/java/com/zaojiao/app/core/event/Flowx.kt
  9. 17 0
      core/event/src/test/java/com/zaojiao/app/core/event/ExampleUnitTest.kt
  10. 1 0
      core/http/build.gradle.kts
  11. 3 1
      core/http/src/main/kotlin/com/zaojiao/app/core/http/di/HttpModule.kt
  12. 13 0
      core/json/build.gradle.kts
  13. 4 0
      core/json/src/main/AndroidManifest.xml
  14. 2 2
      core/json/src/main/java/com/zaojiao/app/core/json/adapter/ColorAdapter.kt
  15. 22 0
      core/json/src/main/java/com/zaojiao/app/core/json/adapter/StateIntAdapter.kt
  16. 1 2
      core/navx/src/main/java/com/zaojiao/app/core/navx/NavXUtils.kt
  17. 15 0
      data/database/build.gradle.kts
  18. 4 0
      data/database/src/main/AndroidManifest.xml
  19. 2 0
      data/model/build.gradle.kts
  20. 6 0
      data/model/src/main/kotlin/com/zaojiao/app/data/model/common/CategoryModel.kt
  21. 17 0
      data/model/src/main/kotlin/com/zaojiao/app/data/model/studyplan/StudyPlanTodayModel.kt
  22. 12 0
      data/model/src/main/kotlin/com/zaojiao/app/data/model/studyplan/StudyPlanTomorrowModel.kt
  23. 16 0
      data/remote/src/main/kotlin/com/zaojiao/app/data/remote/RemoteStudyPlanData.kt
  24. 0 4
      data/remote/src/main/kotlin/com/zaojiao/app/data/remote/RemoteUserData.kt
  25. 4 0
      data/remote/src/main/kotlin/com/zaojiao/app/data/remote/api/CourseApi.kt
  26. 18 0
      data/remote/src/main/kotlin/com/zaojiao/app/data/remote/api/StudyPlanApi.kt
  27. 0 5
      data/remote/src/main/kotlin/com/zaojiao/app/data/remote/api/UserApi.kt
  28. 10 0
      data/remote/src/main/kotlin/com/zaojiao/app/data/remote/di/ApiModule.kt
  29. 4 0
      data/repo/src/main/kotlin/com/zaojiao/app/data/repo/HomeRepository.kt
  30. 10 0
      data/repo/src/main/kotlin/com/zaojiao/app/data/repo/StudyPlanRepository.kt
  31. 8 0
      data/repo/src/main/kotlin/com/zaojiao/app/data/repo/di/RepoModule.kt
  32. 5 0
      data/repo/src/main/kotlin/com/zaojiao/app/data/repo/impl/HomeRepositoryImpl.kt
  33. 26 0
      data/repo/src/main/kotlin/com/zaojiao/app/data/repo/impl/StudyPlanRepositoryImpl.kt
  34. 1 0
      feat/design/build.gradle.kts
  35. 63 0
      feat/design/src/main/kotlin/com/zaojiao/app/feat/design/viewmodel/BaseViewModel.kt
  36. 17 6
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexPage.kt
  37. 4 7
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexSwiper.kt
  38. 4 7
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexSwiper2.kt
  39. 15 19
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexViewModel.kt
  40. 5 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/state/HomeIndexPageUiState.kt
  41. 5 1
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalService.kt
  42. 47 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanIndexPage.kt
  43. 44 25
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanPage.kt
  44. 0 7
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanState.kt
  45. 20 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanTotalPage.kt
  46. 16 25
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanViewModel.kt
  47. 10 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/state/HomePlanIndexUiState.kt
  48. 5 0
      feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/state/HomePlanTotalUiState.kt
  49. 1 1
      feat/settings/src/main/java/com/zaojiao/app/feat/settings/SettingsNavigation.kt
  50. 1 4
      feat/settings/src/main/java/com/zaojiao/app/feat/settings/SettingsPage.kt
  51. 2 0
      feat/settings/src/main/java/com/zaojiao/app/feat/settings/SettingsViewModel.kt
  52. 3 0
      settings.gradle.kts

+ 3 - 0
.idea/gradle.xml

@@ -29,10 +29,13 @@
             <option value="$PROJECT_DIR$/core" />
             <option value="$PROJECT_DIR$/core/auth" />
             <option value="$PROJECT_DIR$/core/common" />
+            <option value="$PROJECT_DIR$/core/event" />
             <option value="$PROJECT_DIR$/core/http" />
+            <option value="$PROJECT_DIR$/core/json" />
             <option value="$PROJECT_DIR$/core/nav" />
             <option value="$PROJECT_DIR$/core/navx" />
             <option value="$PROJECT_DIR$/data" />
+            <option value="$PROJECT_DIR$/data/database" />
             <option value="$PROJECT_DIR$/data/domain" />
             <option value="$PROJECT_DIR$/data/local" />
             <option value="$PROJECT_DIR$/data/model" />

+ 5 - 0
built/convention/build.gradle.kts

@@ -55,6 +55,11 @@ gradlePlugin {
             implementationClass = "LifecycleConventionPlugin"
         }
 
+        register("lifecycleCore") {
+            id = "d.convention.lifecycle.core"
+            implementationClass = "LifecycleCoreConventionPlugin"
+        }
+
         register("navigation") {
             id = "d.convention.navigation"
             implementationClass = "NavigationConventionPlugin"

+ 17 - 0
built/convention/src/main/kotlin/LifecycleConventionPlugin.kt

@@ -24,4 +24,21 @@ class LifecycleConventionPlugin: Plugin<Project> {
             }
         }
     }
+}
+
+class LifecycleCoreConventionPlugin: Plugin<Project> {
+    private val lifecycle_version = "2.5.1"
+
+    override fun apply(target: Project) {
+        with(target) {
+            with(pluginManager) {
+                apply("org.jetbrains.kotlin.kapt")
+            }
+
+            dependencies {
+                add("implementation","androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version")
+                add("kapt","androidx.lifecycle:lifecycle-compiler:$lifecycle_version")
+            }
+        }
+    }
 }

+ 9 - 0
core/event/build.gradle.kts

@@ -0,0 +1,9 @@
+plugins {
+    id("d.convention.library")
+    id("d.convention.coroutines")
+    id("d.convention.lifecycle.core")
+}
+
+android {
+    namespace = "com.zaojiao.app.core.event"
+}

+ 24 - 0
core/event/src/androidTest/java/com/zaojiao/app/core/event/ExampleInstrumentedTest.kt

@@ -0,0 +1,24 @@
+package com.zaojiao.app.core.event
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+    @Test
+    fun useAppContext() {
+        // Context of the app under test.
+        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+        assertEquals("com.zaojiao.app.core.event.test", appContext.packageName)
+    }
+}

+ 4 - 0
core/event/src/main/AndroidManifest.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>

+ 8 - 0
core/event/src/main/java/com/zaojiao/app/core/event/Event.kt

@@ -0,0 +1,8 @@
+package com.zaojiao.app.core.event
+
+sealed interface Event {
+    object Login : Event
+    object Logout : Event
+
+    data class CourseBuy(val id: String) : Event
+}

+ 41 - 0
core/event/src/main/java/com/zaojiao/app/core/event/Flowx.kt

@@ -0,0 +1,41 @@
+package com.zaojiao.app.core.event
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.whenStateAtLeast
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.launch
+import java.util.concurrent.ConcurrentHashMap
+
+object Flowx {
+    private val flowEvents = ConcurrentHashMap<String, MutableSharedFlow<Event>>()
+
+    fun getFlow(key: String): MutableSharedFlow<Event> {
+        return flowEvents[key] ?: MutableSharedFlow<Event>().also { flowEvents[key] = it }
+    }
+
+    fun post(event: Event, delay: Long = 0) {
+        MainScope().launch {
+            delay(delay)
+            getFlow(event.javaClass.simpleName).emit(event)
+        }
+    }
+
+    inline fun <reified T : Event> observe(
+        lifecycleOwner: LifecycleOwner,
+        minState: Lifecycle.State = Lifecycle.State.CREATED,
+        dispatcher: CoroutineDispatcher = Dispatchers.Main,
+        crossinline onReceived: (T) -> Unit
+    ) = lifecycleOwner.lifecycleScope.launch(dispatcher) {
+        getFlow(T::class.java.simpleName).collect {
+            lifecycleOwner.lifecycle.whenStateAtLeast(minState) {
+                if (it is T) onReceived(it)
+            }
+        }
+    }
+}

+ 17 - 0
core/event/src/test/java/com/zaojiao/app/core/event/ExampleUnitTest.kt

@@ -0,0 +1,17 @@
+package com.zaojiao.app.core.event
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+    @Test
+    fun addition_isCorrect() {
+        assertEquals(4, 2 + 2)
+    }
+}

+ 1 - 0
core/http/build.gradle.kts

@@ -10,6 +10,7 @@ android {
 dependencies {
     implementation(project(":core:common"))
     implementation(project(":core:auth"))
+    implementation(project(":core:json"))
 
     implementation(libs.coil.kt)
     implementation(libs.coil.kt.svg)

+ 3 - 1
core/http/src/main/kotlin/com/zaojiao/app/core/http/di/HttpModule.kt

@@ -6,7 +6,8 @@ import coil.decode.SvgDecoder
 import com.squareup.moshi.Moshi
 import com.squareup.moshi.adapters.Rfc3339DateJsonAdapter
 import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
-import com.zaojiao.app.core.http.adapter.HexColorAdapter
+import com.zaojiao.app.core.json.adapter.HexColorAdapter
+import com.zaojiao.app.core.json.adapter.StateIntAdapter
 import com.zaojiao.app.core.http.converter.ResultConverterFactory
 import com.zaojiao.app.core.http.interceptor.AuthInterceptor
 import com.zaojiao.app.core.http.interceptor.ResultInterceptor
@@ -44,6 +45,7 @@ object HttpModule {
     fun provideMoshi(): Moshi {
         return Moshi.Builder()
             .add(HexColorAdapter())
+            .add(StateIntAdapter())
             .add(Date::class.java, Rfc3339DateJsonAdapter())
             .add(KotlinJsonAdapterFactory())
             .build()

+ 13 - 0
core/json/build.gradle.kts

@@ -0,0 +1,13 @@
+plugins {
+    id("d.convention.library")
+}
+
+android {
+    namespace = "com.zaojiao.app.core.json"
+}
+
+dependencies {
+    api("com.squareup.moshi:moshi:1.15.0")
+    api("com.squareup.moshi:moshi-kotlin:1.15.0")
+    api("com.squareup.moshi:moshi-adapters:1.14.0")
+}

+ 4 - 0
core/json/src/main/AndroidManifest.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>

+ 2 - 2
core/http/src/main/kotlin/com/zaojiao/app/core/http/adapter/ColorAdapter.kt → core/json/src/main/java/com/zaojiao/app/core/json/adapter/ColorAdapter.kt

@@ -1,4 +1,4 @@
-package com.zaojiao.app.core.http.adapter
+package com.zaojiao.app.core.json.adapter
 
 import com.squareup.moshi.FromJson
 import com.squareup.moshi.JsonQualifier
@@ -8,7 +8,7 @@ import com.squareup.moshi.ToJson
 @JsonQualifier
 annotation class HexColor
 
-internal class  HexColorAdapter {
+class  HexColorAdapter {
     @ToJson
     fun toJson(@HexColor rgb: Int): String {
         return "#%06x".format(rgb)

+ 22 - 0
core/json/src/main/java/com/zaojiao/app/core/json/adapter/StateIntAdapter.kt

@@ -0,0 +1,22 @@
+package com.zaojiao.app.core.json.adapter
+
+import com.squareup.moshi.FromJson
+import com.squareup.moshi.JsonQualifier
+import com.squareup.moshi.ToJson
+
+@Retention(value = AnnotationRetention.RUNTIME)
+@JsonQualifier
+annotation class StateInt
+
+class StateIntAdapter {
+    @ToJson
+    fun toJson(@StateInt state: Boolean): Int {
+        return if (state) 1 else 0
+    }
+
+    @FromJson
+    @StateInt
+    fun fromJson(state: Int): Boolean {
+        return state == 1
+    }
+}

+ 1 - 2
core/navx/src/main/java/com/zaojiao/app/core/navx/NavXUtils.kt

@@ -14,8 +14,7 @@ object NavXUtils {
         if (needLogin && AuthUtils.state == AuthState.OUT) {
             AuthUtils.requestLogin()
         } else {
-            AuthUtils.requestLogin()
-//            LJGNavigator.navigate(route)
+            LJGNavigator.navigate(route)
         }
     }
 

+ 15 - 0
data/database/build.gradle.kts

@@ -0,0 +1,15 @@
+plugins {
+    id("d.convention.library")
+    id("d.convention.hilt")
+}
+
+android {
+    namespace = "com.zaojiao.app.data.database"
+}
+
+dependencies {
+    implementation(project(":core:common"))
+    implementation(project(":core:auth"))
+
+    implementation(project(":data:model"))
+}

+ 4 - 0
data/database/src/main/AndroidManifest.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>

+ 2 - 0
data/model/build.gradle.kts

@@ -8,6 +8,8 @@ android {
 }
 
 dependencies {
+    implementation(project(":core:json"))
+
     implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
     implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
     implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.5.1")

+ 6 - 0
data/model/src/main/kotlin/com/zaojiao/app/data/model/common/CategoryModel.kt

@@ -0,0 +1,6 @@
+package com.zaojiao.app.data.model.common
+
+data class CategoryModel(
+    val id: String,
+    val name: String,
+)

+ 17 - 0
data/model/src/main/kotlin/com/zaojiao/app/data/model/studyplan/StudyPlanTodayModel.kt

@@ -0,0 +1,17 @@
+package com.zaojiao.app.data.model.studyplan
+
+import com.zaojiao.app.core.json.adapter.StateInt
+
+data class StudyPlanTodayModel(
+    val completeTaskNum: Int,
+    val courseCategoryName: String,
+    val courseItemId: String,
+    val courseItemImgCover: String,
+    val courseItemName: String,
+    val courseName: String,
+    val totalTask: Int,
+    val unlockDate: String,
+    @StateInt val unlockState: Boolean,
+    val unlockTime: String,
+    val unlockWeek: String
+)

+ 12 - 0
data/model/src/main/kotlin/com/zaojiao/app/data/model/studyplan/StudyPlanTomorrowModel.kt

@@ -0,0 +1,12 @@
+package com.zaojiao.app.data.model.studyplan
+
+data class StudyPlanTomorrowModel(
+    val courseItemId: String,
+    val courseItemImgCover: String,
+    val courseItemName: String,
+    val courseName: String,
+    val courseSkuId: String,
+    val courseSpuId: String,
+    val tomorrowUnlock: Int,
+    val unlockDate: String,
+)

+ 16 - 0
data/remote/src/main/kotlin/com/zaojiao/app/data/remote/RemoteStudyPlanData.kt

@@ -0,0 +1,16 @@
+package com.zaojiao.app.data.remote
+
+import com.zaojiao.app.data.remote.api.StudyPlanApi
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class RemoteStudyPlanData @Inject constructor(
+    private val studyPlanApi: StudyPlanApi,
+) {
+    suspend fun getTodayPlan() = studyPlanApi.getTodayPlan().data
+
+    suspend fun getTomorrowPlan() = studyPlanApi.getTomorrowPlan().data
+
+    suspend fun getTotalCategory() = studyPlanApi.getTotalCategory().data
+}

+ 0 - 4
data/remote/src/main/kotlin/com/zaojiao/app/data/remote/RemoteUserData.kt

@@ -13,10 +13,6 @@ import javax.inject.Singleton
 class RemoteUserData @Inject constructor(
     private val userApi: UserApi,
 ) {
-    suspend fun getUser(): DataState<UserGetV2Model> {
-        return dataStateCatch { userApi.getUser() }
-    }
-
     suspend fun getUserInfo(): DataState<UserModel> {
         return dataStateCatch { userApi.getUserInfo() }
     }

+ 4 - 0
data/remote/src/main/kotlin/com/zaojiao/app/data/remote/api/CourseApi.kt

@@ -0,0 +1,4 @@
+package com.zaojiao.app.data.remote.api
+
+class CourseApi {
+}

+ 18 - 0
data/remote/src/main/kotlin/com/zaojiao/app/data/remote/api/StudyPlanApi.kt

@@ -0,0 +1,18 @@
+package com.zaojiao.app.data.remote.api
+
+import com.zaojiao.app.core.http.common.NetResult
+import com.zaojiao.app.data.model.common.CategoryModel
+import com.zaojiao.app.data.model.studyplan.StudyPlanTodayModel
+import com.zaojiao.app.data.model.studyplan.StudyPlanTomorrowModel
+import retrofit2.http.GET
+
+interface StudyPlanApi {
+    @GET("/app/game-course/plan/today")
+    suspend fun getTodayPlan(): NetResult<List<StudyPlanTodayModel>>
+
+    @GET("/app/game-course/plan/tomorrow")
+    suspend fun getTomorrowPlan(): NetResult<List<StudyPlanTomorrowModel>>
+
+    @GET("/app/game-course/plan/category")
+    suspend fun getTotalCategory(): NetResult<List<CategoryModel>>
+}

+ 0 - 5
data/remote/src/main/kotlin/com/zaojiao/app/data/remote/api/UserApi.kt

@@ -8,14 +8,9 @@ import retrofit2.http.GET
 
 
 interface UserApi {
-
-    @GET("/app/user/getV2")
-    suspend fun getUser(): NetResult<UserGetV2Model>
-
     @GET("/app/user/info")
     suspend fun getUserInfo(): NetResult<UserModel>
 
-
     @GET("/app/user/attach/count")
     suspend fun getUserInactive(): NetResult<InteractiveCountModel>
 }

+ 10 - 0
data/remote/src/main/kotlin/com/zaojiao/app/data/remote/di/ApiModule.kt

@@ -2,12 +2,14 @@ package com.zaojiao.app.data.remote.di
 
 import com.zaojiao.app.data.remote.api.BabyApi
 import com.zaojiao.app.data.remote.api.LoginApi
+import com.zaojiao.app.data.remote.api.StudyPlanApi
 import com.zaojiao.app.data.remote.api.UserApi
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn
 import dagger.hilt.components.SingletonComponent
 import retrofit2.Retrofit
+import retrofit2.create
 import javax.inject.Singleton
 
 @Module
@@ -37,4 +39,12 @@ class ApiModule {
     ): LoginApi {
         return retrofit.create(LoginApi::class.java)
     }
+
+    @Singleton
+    @Provides
+    fun provideStudyPlanApi(
+        retrofit: Retrofit
+    ): StudyPlanApi {
+        return retrofit.create(StudyPlanApi::class.java)
+    }
 }

+ 4 - 0
data/repo/src/main/kotlin/com/zaojiao/app/data/repo/HomeRepository.kt

@@ -1,4 +1,8 @@
 package com.zaojiao.app.data.repo
 
+import com.zaojiao.app.data.model.micropage.MicroPageJsonModel
+import kotlinx.coroutines.flow.Flow
+
 interface HomeRepository {
+    fun getHomeIndexData(): Flow<List<MicroPageJsonModel>>
 }

+ 10 - 0
data/repo/src/main/kotlin/com/zaojiao/app/data/repo/StudyPlanRepository.kt

@@ -0,0 +1,10 @@
+package com.zaojiao.app.data.repo
+
+import com.zaojiao.app.data.model.studyplan.StudyPlanTodayModel
+import com.zaojiao.app.data.model.studyplan.StudyPlanTomorrowModel
+
+interface StudyPlanRepository {
+    suspend fun getTodayPlanList(): List<StudyPlanTodayModel>
+
+    suspend fun getTomorrowPlanList(): List<StudyPlanTomorrowModel>
+}

+ 8 - 0
data/repo/src/main/kotlin/com/zaojiao/app/data/repo/di/RepoModule.kt

@@ -2,9 +2,11 @@ package com.zaojiao.app.data.repo.di
 
 import com.zaojiao.app.data.repo.BabyRepository
 import com.zaojiao.app.data.repo.HomeRepository
+import com.zaojiao.app.data.repo.StudyPlanRepository
 import com.zaojiao.app.data.repo.UserRepository
 import com.zaojiao.app.data.repo.impl.BabyRepositoryImpl
 import com.zaojiao.app.data.repo.impl.HomeRepositoryImpl
+import com.zaojiao.app.data.repo.impl.StudyPlanRepositoryImpl
 import com.zaojiao.app.data.repo.impl.UserRepositoryImpl
 import dagger.Binds
 import dagger.Module
@@ -32,4 +34,10 @@ interface RepoModule {
     fun bindHomeRepository(
         homeRepositoryImpl: HomeRepositoryImpl,
     ): HomeRepository
+
+    @Binds
+    @Singleton
+    fun bindStudyPlanRepository(
+        studyPlanRepositoryImpl: StudyPlanRepositoryImpl,
+    ): StudyPlanRepository
 }

+ 5 - 0
data/repo/src/main/kotlin/com/zaojiao/app/data/repo/impl/HomeRepositoryImpl.kt

@@ -1,9 +1,14 @@
 package com.zaojiao.app.data.repo.impl
 
+import com.zaojiao.app.data.model.micropage.MicroPageJsonModel
 import com.zaojiao.app.data.repo.HomeRepository
+import kotlinx.coroutines.flow.Flow
 import javax.inject.Inject
 
 class HomeRepositoryImpl @Inject constructor(
 
 ) : HomeRepository {
+    override fun getHomeIndexData(): Flow<List<MicroPageJsonModel>> {
+        TODO("Not yet implemented")
+    }
 }

+ 26 - 0
data/repo/src/main/kotlin/com/zaojiao/app/data/repo/impl/StudyPlanRepositoryImpl.kt

@@ -0,0 +1,26 @@
+package com.zaojiao.app.data.repo.impl
+
+import com.zaojiao.app.data.model.studyplan.StudyPlanTodayModel
+import com.zaojiao.app.data.model.studyplan.StudyPlanTomorrowModel
+import com.zaojiao.app.data.remote.RemoteStudyPlanData
+import com.zaojiao.app.data.repo.StudyPlanRepository
+import javax.inject.Inject
+
+
+class StudyPlanRepositoryImpl @Inject constructor(
+    private val remoteStudyPlanData: RemoteStudyPlanData,
+) : StudyPlanRepository {
+    override suspend fun getTodayPlanList(): List<StudyPlanTodayModel> {
+        return remoteStudyPlanData.getTodayPlan().run {
+            this ?: emptyList()
+        }
+    }
+
+
+    override suspend fun getTomorrowPlanList(): List<StudyPlanTomorrowModel> {
+        return remoteStudyPlanData.getTomorrowPlan().run {
+            this ?: emptyList()
+        }
+    }
+
+}

+ 1 - 0
feat/design/build.gradle.kts

@@ -1,6 +1,7 @@
 plugins {
     id("d.convention.library")
     id("d.convention.compose")
+    id("d.convention.lifecycle")
 }
 
 android {

+ 63 - 0
feat/design/src/main/kotlin/com/zaojiao/app/feat/design/viewmodel/BaseViewModel.kt

@@ -0,0 +1,63 @@
+package com.zaojiao.app.feat.design.viewmodel
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.zaojiao.app.feat.design.UiState
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.cancel
+import kotlinx.coroutines.launch
+
+abstract class BaseViewModel<T>(
+    private val autoInitial: Boolean = true,
+    private val autoLoading: Boolean = true,
+) : ViewModel() {
+    private val _stateFlow = MutableStateFlow<UiState<T>>(UiState.Loading)
+    val stateFlow: StateFlow<UiState<T>> get() = _stateFlow
+
+    init {
+        firstInit()
+    }
+
+    abstract suspend fun initData(): T
+
+    protected fun firstInit() {
+        if (autoInitial) {
+            viewModelScope.launch {
+                if (autoLoading) {
+                    setLoading()
+                }
+                try {
+                    val result = initData()
+                    setSuccess(result)
+                } catch (e: Throwable) {
+                    e.printStackTrace()
+                    setFailure(0)
+                }
+            }
+        }
+    }
+
+    protected fun refresh() =
+        viewModelScope.launch {
+            try {
+                val result = initData()
+                setSuccess(result)
+            } catch (e: Throwable) {
+                setFailure(0)
+            }
+        }
+
+
+    protected suspend fun setLoading() {
+        _stateFlow.emit(UiState.Loading)
+    }
+
+    protected suspend fun setSuccess(data: T) {
+        _stateFlow.emit(UiState.Success(data))
+    }
+
+    protected suspend fun setFailure(code: Int) {
+        _stateFlow.emit(UiState.Failure(code))
+    }
+}

+ 17 - 6
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexPage.kt

@@ -14,9 +14,11 @@ import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
 import androidx.hilt.navigation.compose.hiltViewModel
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.zaojiao.app.feat.design.StatePage
 import com.zaojiao.app.feat.design.grid
 import com.zaojiao.app.feat.design.list
 import com.zaojiao.app.feat.home.index.state.HomeIndexBabyUiState
+import com.zaojiao.app.feat.home.index.state.HomeIndexPageUiState
 
 
 @Composable
@@ -24,17 +26,22 @@ internal fun HomeIndexRoute(
     viewModel: HomeIndexViewModel = hiltViewModel(),
 ) {
     val babyUiState by viewModel.babyUiState.collectAsStateWithLifecycle()
+    val indexUiState by viewModel.stateFlow.collectAsStateWithLifecycle()
 
-    HomeIndexPage(
-        babyUiState = babyUiState,
-        onBabyInfoClick = { viewModel.navToUserPage() }
-    )
+    StatePage(uiState = indexUiState) {
+        HomeIndexPage(
+            babyUiState = babyUiState,
+            indexUiState = this,
+            onBabyInfoClick = { }
+        )
+    }
 }
 
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun HomeIndexPage(
     babyUiState: HomeIndexBabyUiState,
+    indexUiState: HomeIndexPageUiState,
     onBabyInfoClick: () -> Unit,
 ) {
     LazyColumn(
@@ -51,7 +58,9 @@ fun HomeIndexPage(
         }
 
         item {
-            HomeIndexSwiper()
+            HomeIndexSwiper(
+                list = indexUiState.bannerList,
+            )
         }
 
         item {
@@ -59,7 +68,9 @@ fun HomeIndexPage(
         }
 
         item {
-            HomeIndexSwiper2()
+            HomeIndexSwiper2(
+                list = indexUiState.bannerList,
+                )
         }
 
         item {

+ 4 - 7
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexSwiper.kt

@@ -13,14 +13,11 @@ import com.zaojiao.app.feat.design.LJGBanner
 
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
-fun HomeIndexSwiper() {
+fun HomeIndexSwiper(
+    list: List<String>,
+) {
     LJGBanner(
-        data = listOf(
-            "https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF",
-            "https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF",
-            "https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF",
-            "https://t7.baidu.com/it/u=2529476510,3041785782&fm=193&f=GIF",
-        ),
+        data = list,
         onImagePath = { _, item ->
             item
         },

+ 4 - 7
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexSwiper2.kt

@@ -13,14 +13,11 @@ import com.zaojiao.app.feat.design.LJGBanner
 
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
-fun HomeIndexSwiper2() {
+fun HomeIndexSwiper2(
+    list: List<String>,
+) {
     LJGBanner(
-        data = listOf(
-            "https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF",
-            "https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF",
-            "https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF",
-            "https://t7.baidu.com/it/u=2529476510,3041785782&fm=193&f=GIF",
-        ),
+        data = list,
         onImagePath = { _, item ->
             item
         },

+ 15 - 19
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/HomeIndexViewModel.kt

@@ -1,24 +1,25 @@
 package com.zaojiao.app.feat.home.index
 
-import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import com.zaojiao.app.core.auth.data.AuthRepository
 import com.zaojiao.app.core.auth.utils.AuthState
+import com.zaojiao.app.feat.design.viewmodel.BaseViewModel
 import com.zaojiao.app.data.repo.BabyRepository
 import com.zaojiao.app.feat.home.index.state.HomeIndexBabyUiState
+import com.zaojiao.app.feat.home.index.state.HomeIndexPageUiState
 import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
 import javax.inject.Inject
 
 @HiltViewModel
 class HomeIndexViewModel @Inject constructor(
     private val authRepository: AuthRepository,
     private val babyRepository: BabyRepository,
-) : ViewModel() {
+) : BaseViewModel<HomeIndexPageUiState>() {
     val babyUiState: StateFlow<HomeIndexBabyUiState> =
         combine(authRepository.state, babyRepository.current) { auth, baby ->
             when (auth) {
@@ -44,21 +45,16 @@ class HomeIndexViewModel @Inject constructor(
             initialValue = HomeIndexBabyUiState.None,
         )
 
-    fun navToUserPage() = viewModelScope.launch {
-//        val isLogin = authRepository.checkHasLogin()
-//
-//        if (isLogin) {
-//            LJGNavigator.toBabyIndex()
-//        } else {
-//            thread {
-//                AuthUtils.navToLogin().apply {
-//                    if (this != null) {
-//                        LJGNavigator.toBabyIndex()
-//                    } else {
-//
-//                    }
-//                }
-//            }
-//        }
+    override suspend fun initData(): HomeIndexPageUiState {
+        val list = listOf(
+            "https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF",
+            "https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF",
+            "https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF",
+            "https://t7.baidu.com/it/u=2529476510,3041785782&fm=193&f=GIF",
+        )
+
+        return HomeIndexPageUiState(
+            bannerList = list
+        )
     }
 }

+ 5 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/index/state/HomeIndexPageUiState.kt

@@ -0,0 +1,5 @@
+package com.zaojiao.app.feat.home.index.state
+
+data class HomeIndexPageUiState(
+    val bannerList: List<String> = emptyList()
+)

+ 5 - 1
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/personal/HomePersonalService.kt

@@ -4,6 +4,7 @@ import androidx.annotation.DrawableRes
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -141,7 +142,10 @@ fun RowScope.HomePersonalServiceItem(
 ) {
     Column(
         modifier = Modifier
-            .clickable { onClick?.invoke() }
+            .clickable(
+                indication = null,
+                interactionSource = MutableInteractionSource(),
+            ) { onClick?.invoke() }
             .weight(1f)
             .fillMaxHeight()
             .widthIn(max = 64.dp),

+ 47 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanIndexPage.kt

@@ -0,0 +1,47 @@
+package com.zaojiao.app.feat.home.plan
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.zaojiao.app.data.model.studyplan.StudyPlanTodayModel
+import com.zaojiao.app.data.model.studyplan.StudyPlanTomorrowModel
+import com.zaojiao.app.feat.design.StatePage
+
+@Composable
+fun HomePlanIndexPage(
+    viewModel: HomePlanViewModel = hiltViewModel()
+) {
+    val uiState by viewModel.stateFlow.collectAsStateWithLifecycle()
+
+    StatePage(uiState = uiState) {
+        LazyColumn(
+            modifier = Modifier.fillMaxWidth()
+        ) {
+            item {
+                HomePlanTomorrow(tomorrowPlanList = this@StatePage.tomorrowPlanList)
+            }
+
+            item{
+                HomePlanToday(todayPlanList = this@StatePage.todayPlanList)
+            }
+        }
+    }
+}
+
+@Composable
+internal fun HomePlanTomorrow(
+    tomorrowPlanList: List<StudyPlanTomorrowModel>
+) {
+
+}
+
+@Composable
+internal fun HomePlanToday(
+    todayPlanList: List<StudyPlanTodayModel>
+) {
+
+}

+ 44 - 25
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanPage.kt

@@ -1,41 +1,60 @@
 package com.zaojiao.app.feat.home.plan
 
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.material3.Tab
+import androidx.compose.material3.TabRow
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.hilt.navigation.compose.hiltViewModel
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.zaojiao.app.feat.design.StatePage
-import com.zaojiao.app.feat.design.UiState
+import kotlinx.coroutines.launch
 
 @Composable
-internal fun HomePlanRoute(
-    viewModel: HomePlanViewModel = hiltViewModel(),
-) {
-    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
-
-    HomePlanPage(uiState = uiState)
+internal fun HomePlanRoute() {
+    HomePlanPage()
 }
 
+@OptIn(ExperimentalFoundationApi::class)
 @Composable
-fun HomePlanPage(uiState: UiState<HomePlanState>) {
-    StatePage(uiState = uiState) {
-        Column(
-            modifier = Modifier
-                .fillMaxSize()
-                .background(color = Color.Green)
+fun HomePlanPage() {
+    val tabTitles = listOf("学习计划", "全部课程")
+    val pageState = rememberPagerState()
+    val coroutineScope = rememberCoroutineScope()
+
+    Column(
+        modifier = Modifier
+            .statusBarsPadding()
+            .fillMaxSize()
+    ) {
+        TabRow(selectedTabIndex = pageState.currentPage) {
+            tabTitles.forEachIndexed { index, title ->
+                Tab(selected = pageState.currentPage == index,
+                    onClick = {
+                        coroutineScope.launch {
+                            pageState.scrollToPage(index)
+
+                        }
+                    }) {
+                    Text(text = title)
+                }
+            }
+        }
+
+        HorizontalPager(
+            state = pageState,
+            pageCount = 2,
+            userScrollEnabled = true,
         ) {
-            todayPlanList.forEach {
-                Text(
-                    text = "abcdefg",
-                )
+            when (it) {
+                0 -> HomePlanIndexPage()
+                1 -> HomePlanTotalPage()
+                else -> TODO()
             }
         }
     }

+ 0 - 7
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanState.kt

@@ -1,7 +0,0 @@
-package com.zaojiao.app.feat.home.plan
-
-data class HomePlanState(
-    val todayPlanList: List<String> = emptyList(),
-    val tomorrowPlanList: List<String> = emptyList(),
-    val historyPlanList: List<String> = emptyList(),
-)

+ 20 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanTotalPage.kt

@@ -0,0 +1,20 @@
+package com.zaojiao.app.feat.home.plan
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.hilt.navigation.compose.hiltViewModel
+
+@Composable
+fun HomePlanTotalPage(
+    viewModel: HomePlanViewModel = hiltViewModel()
+) {
+    Box(
+        modifier = Modifier
+            .background(color = Color.Green)
+            .fillMaxSize(),
+    )
+}

+ 16 - 25
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/HomePlanViewModel.kt

@@ -1,31 +1,22 @@
 package com.zaojiao.app.feat.home.plan
 
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.zaojiao.app.feat.design.UiState
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.flow.stateIn
+import com.zaojiao.app.data.repo.StudyPlanRepository
+import com.zaojiao.app.feat.design.viewmodel.BaseViewModel
+import com.zaojiao.app.feat.home.plan.state.HomePlanIndexUiState
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
 
-class HomePlanViewModel(
+@HiltViewModel
+class HomePlanViewModel @Inject constructor(
+    private val studyPlanRepository: StudyPlanRepository,
+) : BaseViewModel<HomePlanIndexUiState>() {
+    override suspend fun initData(): HomePlanIndexUiState {
+        val todayPlanList = studyPlanRepository.getTodayPlanList()
+        val tomorrowPlanList = studyPlanRepository.getTomorrowPlanList()
 
-) : ViewModel() {
-    val uiState: StateFlow<UiState<HomePlanState>> = flow<UiState<HomePlanState>> {
-        delay(3000)
-        emit(
-            UiState.Success(
-                HomePlanState(
-                    todayPlanList = listOf(
-                        "a", "b", "c"
-                    )
-                )
-            )
+        return HomePlanIndexUiState(
+            todayPlanList = todayPlanList,
+            tomorrowPlanList = tomorrowPlanList,
         )
-    }.stateIn(
-        scope = viewModelScope,
-        started = SharingStarted.WhileSubscribed(5_000),
-        initialValue = UiState.Loading,
-    )
+    }
 }

+ 10 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/state/HomePlanIndexUiState.kt

@@ -0,0 +1,10 @@
+package com.zaojiao.app.feat.home.plan.state
+
+import com.zaojiao.app.data.model.studyplan.StudyPlanTodayModel
+import com.zaojiao.app.data.model.studyplan.StudyPlanTomorrowModel
+
+data class HomePlanIndexUiState(
+    val todayPlanList: List<StudyPlanTodayModel> = emptyList(),
+    val tomorrowPlanList: List<StudyPlanTomorrowModel> = emptyList(),
+    val historyPlanList: List<String> = emptyList(),
+)

+ 5 - 0
feat/home/src/main/kotlin/com/zaojiao/app/feat/home/plan/state/HomePlanTotalUiState.kt

@@ -0,0 +1,5 @@
+package com.zaojiao.app.feat.home.plan.state
+
+data class HomePlanTotalUiState(
+    val categoryList: List<String> = emptyList(),
+)

+ 1 - 1
feat/settings/src/main/java/com/zaojiao/app/feat/settings/SettingsNavigation.kt

@@ -12,5 +12,5 @@ fun LJGNavigator.toSettingsIndex(navOptions: NavOptions? = null) {
 }
 
 fun NavGraphBuilder.settingRoute() {
-    composable(route = settingsIndex) { SettingsRoute() }
+    composable(route = settingsIndex) { SettingsIndexRoute() }
 }

+ 1 - 4
feat/settings/src/main/java/com/zaojiao/app/feat/settings/SettingsPage.kt

@@ -3,7 +3,6 @@ package com.zaojiao.app.feat.settings
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
@@ -11,10 +10,8 @@ import androidx.compose.foundation.layout.navigationBarsPadding
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.Button
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.TextStyle
@@ -27,7 +24,7 @@ import com.zaojiao.app.feat.design.AppBar
 import com.zaojiao.app.feat.design.Colors
 
 @Composable
-internal fun SettingsRoute(
+internal fun SettingsIndexRoute(
     viewModel: SettingsViewModel = hiltViewModel()
 ) {
     SettingsPage(

+ 2 - 0
feat/settings/src/main/java/com/zaojiao/app/feat/settings/SettingsViewModel.kt

@@ -3,6 +3,7 @@ package com.zaojiao.app.feat.settings
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import com.zaojiao.app.core.auth.data.AuthRepository
+import com.zaojiao.app.data.repo.UserRepository
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.launch
 import javax.inject.Inject
@@ -10,6 +11,7 @@ import javax.inject.Inject
 @HiltViewModel
 class SettingsViewModel @Inject constructor(
     private val authRepository: AuthRepository,
+    private val userRepository: UserRepository,
 ) : ViewModel() {
 
     fun logout() {

+ 3 - 0
settings.gradle.kts

@@ -23,6 +23,8 @@ include(":app")
 include(":core:common")
 include(":core:http")
 include(":core:auth")
+include(":core:json")
+include(":core:event")
 include(":core:nav")
 include(":core:navx")
 
@@ -36,3 +38,4 @@ include(":feat:design")
 include(":feat:home")
 include(":feat:baby")
 include(":feat:settings")
+include(":data:database")