feat: 复刻小程序四大主页面与底部导航(UI 优先,本地 mock)
This commit is contained in:
@@ -13,4 +13,6 @@ dependencies {
|
||||
implementation(project(":core:network"))
|
||||
implementation(project(":core:database"))
|
||||
implementation(project(":core:datastore"))
|
||||
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.mingyuan.android_client.core.data.di
|
||||
|
||||
import com.mingyuan.android_client.core.data.repository.FakeCheckinRepository
|
||||
import com.mingyuan.android_client.core.data.repository.FakeHomeRepository
|
||||
import com.mingyuan.android_client.core.data.repository.FakePlanRepository
|
||||
import com.mingyuan.android_client.core.data.repository.FakeProfileRepository
|
||||
import com.mingyuan.android_client.core.domain.repository.CheckinRepository
|
||||
import com.mingyuan.android_client.core.domain.repository.HomeRepository
|
||||
import com.mingyuan.android_client.core.domain.repository.PlanRepository
|
||||
import com.mingyuan.android_client.core.domain.repository.ProfileRepository
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class DataModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindHomeRepository(impl: FakeHomeRepository): HomeRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindCheckinRepository(impl: FakeCheckinRepository): CheckinRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindPlanRepository(impl: FakePlanRepository): PlanRepository
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindProfileRepository(impl: FakeProfileRepository): ProfileRepository
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.mingyuan.android_client.core.data.repository
|
||||
|
||||
import com.mingyuan.android_client.core.domain.repository.CheckinRepository
|
||||
import com.mingyuan.android_client.core.model.CheckinRecord
|
||||
import com.mingyuan.android_client.core.model.CheckinStatus
|
||||
import com.mingyuan.android_client.core.model.GroupMember
|
||||
import com.mingyuan.android_client.core.model.PersonalCheckinRecord
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class FakeCheckinRepository @Inject constructor() : CheckinRepository {
|
||||
|
||||
private val record = MutableStateFlow(seedCheckin())
|
||||
|
||||
override fun observeCheckin(): Flow<CheckinRecord> = flow {
|
||||
delay(250)
|
||||
emitAll(record)
|
||||
}
|
||||
|
||||
override suspend fun submitCheckin(status: CheckinStatus) {
|
||||
delay(300)
|
||||
record.update { current ->
|
||||
current.copy(
|
||||
checked = true,
|
||||
status = status,
|
||||
groupMembers = current.groupMembers.map { member ->
|
||||
if (member.id == "me") {
|
||||
member.copy(checkedIn = true, checkinTime = "现在", status = status)
|
||||
} else {
|
||||
member
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun seedCheckin() = CheckinRecord(
|
||||
classDate = "2026-09-09",
|
||||
weekday = "周三",
|
||||
dayNum = 9,
|
||||
checked = false,
|
||||
status = null,
|
||||
groupName = "进阶一组",
|
||||
groupMembers = listOf(
|
||||
GroupMember("m1", "张三", true, "19:02", CheckinStatus.NORMAL),
|
||||
GroupMember("m2", "李四", true, "19:30", CheckinStatus.LATE),
|
||||
GroupMember("me", "我", false, null, null),
|
||||
GroupMember("m3", "王五", true, "09:10", CheckinStatus.LEAVE),
|
||||
GroupMember("m4", "赵六", false, null, null),
|
||||
),
|
||||
personalHistory = listOf(
|
||||
PersonalCheckinRecord("2026-09-06", CheckinStatus.NORMAL, "19:05"),
|
||||
PersonalCheckinRecord("2026-09-02", CheckinStatus.LATE, "19:32"),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.mingyuan.android_client.core.data.repository
|
||||
|
||||
import com.mingyuan.android_client.core.domain.repository.HomeRepository
|
||||
import com.mingyuan.android_client.core.model.HomeData
|
||||
import com.mingyuan.android_client.core.model.HomeModule
|
||||
import com.mingyuan.android_client.core.model.IntroCard
|
||||
import com.mingyuan.android_client.core.model.ModuleDestination
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class FakeHomeRepository @Inject constructor() : HomeRepository {
|
||||
override fun observeHome(): Flow<HomeData> = flow {
|
||||
delay(250)
|
||||
emit(seedHome())
|
||||
}
|
||||
|
||||
private fun seedHome() = HomeData(
|
||||
greeting = "晚上好",
|
||||
displayName = "张三",
|
||||
encouragement = "坚持每周打卡,与小组共同进步。",
|
||||
modules = listOf(
|
||||
HomeModule("checkin", "📋", "打卡介绍", "每日打卡记录", ModuleDestination.CHECKIN),
|
||||
HomeModule("plan", "📝", "计划介绍", "周/月/年计划", ModuleDestination.PLAN),
|
||||
HomeModule("about", "ℹ️", "关于我们", "了解明缘云道场", ModuleDestination.ABOUT),
|
||||
),
|
||||
intros = listOf(
|
||||
IntroCard(
|
||||
"📚 学习成长工具",
|
||||
"明缘云道场是一款专注于个人成长的效率工具,通过每周打卡与计划管理,帮助你养成良好的学习习惯,实现持续进步。好好学习,天天向上。",
|
||||
),
|
||||
IntroCard(
|
||||
"✅ 周打卡",
|
||||
"每周打卡,支持多种状态记录(正常、迟到、请假、早退、不参加讨论等)。查看小组打卡情况,与同学们互相监督、共同进步。",
|
||||
),
|
||||
IntroCard(
|
||||
"📝 计划管理",
|
||||
"提供周计划、月计划、年计划三级管理,支持文字与图片上传。清晰追踪每个周期的目标完成情况,让成长有迹可循。",
|
||||
),
|
||||
IntroCard(
|
||||
"👥 小组共学",
|
||||
"加入学习小组,查看组员的打卡与计划完成情况。在集体的氛围中保持学习动力,与志同道合的伙伴一起成长。",
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.mingyuan.android_client.core.data.repository
|
||||
|
||||
import com.mingyuan.android_client.core.domain.repository.PlanRepository
|
||||
import com.mingyuan.android_client.core.model.MyPlanRecord
|
||||
import com.mingyuan.android_client.core.model.PlanGroup
|
||||
import com.mingyuan.android_client.core.model.PlanMember
|
||||
import com.mingyuan.android_client.core.model.PlanRecord
|
||||
import com.mingyuan.android_client.core.model.PlanSubmitData
|
||||
import com.mingyuan.android_client.core.model.PlanType
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class FakePlanRepository @Inject constructor() : PlanRepository {
|
||||
|
||||
private val plans = PlanType.entries.associateWith { type -> MutableStateFlow(seedPlan(type)) }
|
||||
private val submits = mutableMapOf<String, MutableStateFlow<PlanSubmitData>>()
|
||||
|
||||
override fun observePlan(type: PlanType): Flow<PlanRecord> = flow {
|
||||
delay(250)
|
||||
emitAll(plans.getValue(type))
|
||||
}
|
||||
|
||||
override fun observePlanSubmit(type: PlanType, periodKey: String): Flow<PlanSubmitData> = flow {
|
||||
delay(250)
|
||||
emitAll(submitState(type, periodKey))
|
||||
}
|
||||
|
||||
override suspend fun savePlan(
|
||||
type: PlanType,
|
||||
periodKey: String,
|
||||
content: String,
|
||||
attachments: List<String>,
|
||||
) {
|
||||
delay(300)
|
||||
val submit = submitState(type, periodKey)
|
||||
submit.value = submit.value.copy(content = content, attachments = attachments, checked = true)
|
||||
val plan = plans.getValue(type)
|
||||
plan.value = plan.value.copy(
|
||||
myChecked = true,
|
||||
myRecord = MyPlanRecord("已完成", "现在", content, attachments),
|
||||
)
|
||||
}
|
||||
|
||||
private fun submitState(type: PlanType, periodKey: String): MutableStateFlow<PlanSubmitData> =
|
||||
submits.getOrPut("${type.name}:$periodKey") { MutableStateFlow(seedSubmit(type)) }
|
||||
|
||||
private fun seedPlan(type: PlanType) = PlanRecord(
|
||||
type = type,
|
||||
periodKey = periodKeyFor(type),
|
||||
canSubmit = true,
|
||||
message = if (type == PlanType.WEEKLY) "本周计划提交截止:周日晚 23:59" else null,
|
||||
myChecked = type == PlanType.WEEKLY,
|
||||
myRecord = if (type == PlanType.WEEKLY) {
|
||||
MyPlanRecord("已完成", "今天 18:30", "本周完成两章学习,整理笔记并复习错题。", listOf("图1", "图2"))
|
||||
} else {
|
||||
null
|
||||
},
|
||||
groups = listOf(
|
||||
PlanGroup("g1", "进阶一组", 4, 6, memberSeed()),
|
||||
PlanGroup("g2", "进阶二组", 5, 5, memberSeed()),
|
||||
PlanGroup("g3", "进阶三组", 2, 7, memberSeed()),
|
||||
),
|
||||
unassignedCount = 2,
|
||||
)
|
||||
|
||||
private fun seedSubmit(type: PlanType) = PlanSubmitData(
|
||||
content = if (type == PlanType.WEEKLY) "本周完成两章学习,整理笔记并复习错题。" else "",
|
||||
attachments = if (type == PlanType.WEEKLY) listOf("图1", "图2") else emptyList(),
|
||||
checked = type == PlanType.WEEKLY,
|
||||
canSubmit = true,
|
||||
message = null,
|
||||
history = if (type == PlanType.WEEKLY) {
|
||||
emptyList()
|
||||
} else {
|
||||
listOf(MyPlanRecord("已完成", "2026-08 完成", "上月计划内容……", emptyList()))
|
||||
},
|
||||
)
|
||||
|
||||
private fun periodKeyFor(type: PlanType): String = when (type) {
|
||||
PlanType.WEEKLY -> "2026 第 37 周"
|
||||
PlanType.MONTHLY -> "2026 年 9 月"
|
||||
PlanType.YEARLY -> "2026 年"
|
||||
}
|
||||
|
||||
private fun memberSeed() = listOf(
|
||||
PlanMember("p1", "张三", true),
|
||||
PlanMember("p2", "李四", true),
|
||||
PlanMember("p3", "王五", false),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.mingyuan.android_client.core.data.repository
|
||||
|
||||
import com.mingyuan.android_client.core.domain.repository.ProfileRepository
|
||||
import com.mingyuan.android_client.core.model.ClassGroup
|
||||
import com.mingyuan.android_client.core.model.UserProfile
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class FakeProfileRepository @Inject constructor() : ProfileRepository {
|
||||
|
||||
private val profile = MutableStateFlow(seedProfile())
|
||||
|
||||
override fun observeProfile(): Flow<UserProfile> = flow {
|
||||
delay(250)
|
||||
emitAll(profile)
|
||||
}
|
||||
|
||||
override suspend fun deleteAccount() {
|
||||
delay(300)
|
||||
profile.value = seedProfile().copy(
|
||||
displayName = "游客",
|
||||
name = "",
|
||||
studentId = "",
|
||||
phone = null,
|
||||
group = null,
|
||||
avatarUrl = null,
|
||||
classGroups = emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun seedProfile() = UserProfile(
|
||||
displayName = "张三",
|
||||
name = "张三",
|
||||
studentId = "20260001",
|
||||
phone = "138****1234",
|
||||
role = "成员",
|
||||
group = "进阶一组",
|
||||
loginType = "手机号",
|
||||
avatarUrl = null,
|
||||
classGroups = listOf(
|
||||
ClassGroup("2026 秋季", "进阶一组", "成员"),
|
||||
ClassGroup("2026 春季", "进阶二组", "组长"),
|
||||
),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user