cloudroam
2025-04-01 2309f454c0e1df3c43fde66002a1d009c0b8f479
add : 收入逻辑
已修改7个文件
已添加5个文件
597 ■■■■ 文件已修改
app/src/main/java/com/example/firstapp/activity/PickupActivity.kt 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/adapter/IncomeAdapter.kt 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/database/repository/CodeRepository.kt 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/model/IncomeGroup.kt 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/model/StationGroup.kt 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt 158 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/activity_phone_login.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/fragment_home.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/item_income_group.xml 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/layout/item_income_package_home.xml 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/firstapp/activity/PickupActivity.kt
@@ -14,11 +14,23 @@
class PickupActivity : AppCompatActivity() {
    private lateinit var binding: ActivityPickupBinding
    private lateinit var expressAdapter: ExpressPackageAdapter
    // 添加类型常量
    companion object {
        const val TYPE_EXPRESS = "express"
        const val TYPE_REPAYMENT = "repayment"
        const val TYPE_INCOME = "income"
    }
    private var pageType = TYPE_EXPRESS
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityPickupBinding.inflate(layoutInflater)
        setContentView(binding.root)
        // 获取页面类型
        pageType = intent.getStringExtra("page_type") ?: TYPE_EXPRESS
        initViews()
        loadData()
@@ -39,17 +51,37 @@
            finish()
        }
        // 修改底部按钮点击事件
        binding.btnPickupAll.setOnClickListener {
            showPickupConfirmDialog()
        // 设置底部按钮文本并添加点击事件
        binding.btnPickupAll.apply {
            text = getButtonText()
            setOnClickListener {
                showPickupConfirmDialog()
            }
        }
    }
    private fun getConfirmMessage(): String {
        return when (pageType) {
            TYPE_EXPRESS -> "是否确认取出所有包裹?"
            TYPE_REPAYMENT -> "是否确认处理所有还款?"
            TYPE_INCOME -> "是否确认处理所有收入?"
            else -> "是否确认处理所有项目?"
        }
    }
    private fun getButtonText(): String {
        return when (pageType) {
            TYPE_EXPRESS -> "全部取件"
            TYPE_REPAYMENT -> "全部还款"
            TYPE_INCOME -> "全部收款"
            else -> "全部处理"
        }
    }
    private fun showPickupConfirmDialog() {
        AlertDialog.Builder(this)
            .setTitle("确认取件")
            .setMessage("是否确认取出所有包裹?")
            .setTitle(getButtonText())
            .setMessage(getConfirmMessage())
            .setPositiveButton("确认") { _, _ ->
                handlePickupAll()
            }
@@ -84,7 +116,7 @@
                // 清空列表
                expressAdapter.submitList(emptyList())
                // 更新包裹数量显示
                binding.tvPackageCount.text = "共0个包裹"
                binding.tvPackageCount.text = getCountText(0)
                // 通知MainActivity刷新
                setResult(RESULT_OK)
            } catch (e: Exception) {
@@ -92,6 +124,15 @@
                // 如果出错则重新加载数据
                loadData()
            }
        }
    }
    private fun getCountText(count: Int): String {
        return when (pageType) {
            TYPE_EXPRESS -> "共${count}个包裹"
            TYPE_REPAYMENT -> "共${count}笔还款"
            TYPE_INCOME -> "共${count}笔收入"
            else -> "共${count}个"
        }
    }
@@ -112,7 +153,7 @@
            
            expressAdapter.submitList(packages)
            binding.tvStationName.text = stationName
            binding.tvPackageCount.text = "共${packages.size}个包裹"
            binding.tvPackageCount.text = getCountText(packages.size)
        }
    }
app/src/main/java/com/example/firstapp/adapter/IncomeAdapter.kt
对比新文件
@@ -0,0 +1,111 @@
package com.example.firstapp.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.firstapp.databinding.ItemIncomeGroupBinding
import com.example.firstapp.databinding.ItemIncomePackageHomeBinding
import com.example.firstapp.model.IncomeGroup
import com.example.firstapp.model.IncomePackage
class IncomeAdapter : ListAdapter<IncomeGroup, IncomeAdapter.ViewHolder>(IncomeGroupDiffCallback()) {
    private var onPackageClickListener: (IncomeGroup, IncomePackage) -> Unit = { _, _ -> }
    fun setOnPackageClickListener(listener: (IncomeGroup, IncomePackage) -> Unit) {
        onPackageClickListener = listener
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemIncomeGroupBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
        return ViewHolder(binding)
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val group = getItem(position)
        holder.bind(group)
    }
    inner class ViewHolder(private val binding: ItemIncomeGroupBinding) :
        RecyclerView.ViewHolder(binding.root) {
        private val packagesAdapter = IncomePackageHomeAdapter { pack ->
            currentGroup?.let { group ->
                onPackageClickListener(group, pack)
            }
        }
        private var currentGroup: IncomeGroup? = null
        init {
            binding.rvPackages.apply {
                layoutManager = LinearLayoutManager(context)
                adapter = packagesAdapter
            }
        }
        fun bind(group: IncomeGroup) {
            currentGroup = group
            binding.tvStationName.text = group.stationName
            binding.tvPackageCount.text = "共${group.packages.size}笔收入"
            packagesAdapter.submitList(group.packages)
        }
    }
}
class IncomePackageHomeAdapter(private val onPackageClick: (IncomePackage) -> Unit) :
    ListAdapter<IncomePackage, IncomePackageHomeAdapter.ViewHolder>(IncomePackageDiffCallback()) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemIncomePackageHomeBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
        return ViewHolder(binding)
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val pack = getItem(position)
        holder.bind(pack)
    }
    inner class ViewHolder(private val binding: ItemIncomePackageHomeBinding) :
        RecyclerView.ViewHolder(binding.root) {
        init {
            binding.root.setOnClickListener {
                val pack = getItem(adapterPosition)
                onPackageClick(pack)
            }
        }
        fun bind(pack: IncomePackage) {
            binding.tvCompany.text = pack.company
            binding.tvCreateTime.text = pack.createTime
            binding.tvTrackingNumber.text = "¥${pack.trackingNumber}"
//            binding.tvBalance.text = "余额: ¥${pack.balance}"
        }
    }
}
private class IncomeGroupDiffCallback : DiffUtil.ItemCallback<IncomeGroup>() {
    override fun areItemsTheSame(oldItem: IncomeGroup, newItem: IncomeGroup): Boolean {
        return oldItem.stationName == newItem.stationName
    }
    override fun areContentsTheSame(oldItem: IncomeGroup, newItem: IncomeGroup): Boolean {
        return oldItem == newItem
    }
}
private class IncomePackageDiffCallback : DiffUtil.ItemCallback<IncomePackage>() {
    override fun areItemsTheSame(oldItem: IncomePackage, newItem: IncomePackage): Boolean {
        return oldItem.id == newItem.id
    }
    override fun areContentsTheSame(oldItem: IncomePackage, newItem: IncomePackage): Boolean {
        return oldItem == newItem
    }
}
app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt
@@ -10,6 +10,7 @@
import com.example.firstapp.model.CourierStat
import com.example.firstapp.model.DailyStat
import com.example.firstapp.model.HeatmapStat
import com.example.firstapp.model.StationGroup
import io.reactivex.Completable
import kotlinx.coroutines.flow.Flow
@@ -261,4 +262,21 @@
    WHERE strftime('%Y', createTime) = strftime('%Y', datetime(:date/1000, 'unixepoch', 'localtime'))
""")
    fun getCurrentYearStats(date: Long): Flow<List<DailyStat>>
    @Query("""
        SELECT oneLevel as stationName, COUNT(*) as count
        FROM Code
        WHERE category = :type AND pickup = '0'
        GROUP BY oneLevel
        ORDER BY createTime DESC
    """)
    fun getStationsByType(type: String): List<StationGroup>
    @Query("""
        SELECT * FROM Code
        WHERE category = :type AND pickup = '0'
        AND oneLevel = :stationName
        ORDER BY createTime DESC
    """)
    fun getPackagesByTypeAndStation(type: String, stationName: String): List<Code>
}
app/src/main/java/com/example/firstapp/database/repository/CodeRepository.kt
@@ -3,6 +3,7 @@
import androidx.annotation.WorkerThread
import com.example.firstapp.database.dao.CodeDao
import com.example.firstapp.database.entity.Code
import com.example.firstapp.model.StationGroup
import kotlinx.coroutines.flow.Flow
@@ -75,4 +76,14 @@
    fun getCurrentYearStats(date: Long) = codeDao.getCurrentYearStats(date)
    @WorkerThread
    fun getStationsByType(type: String): List<StationGroup> {
        return codeDao.getStationsByType(type)
    }
    @WorkerThread
    fun getPackagesByTypeAndStation(type: String, stationName: String): List<Code> {
        return codeDao.getPackagesByTypeAndStation(type, stationName)
    }
}
app/src/main/java/com/example/firstapp/model/IncomeGroup.kt
对比新文件
@@ -0,0 +1,14 @@
package com.example.firstapp.model
data class IncomeGroup(
    val stationName: String,  // 银行名称
    val packages: List<IncomePackage>
)
data class IncomePackage(
    var id: Long,
    val company: String,    // 交易对方
    val trackingNumber: String,  // 金额
    val createTime: String,  // 交易时间
    val balance: String     // 账户余额
)
app/src/main/java/com/example/firstapp/model/StationGroup.kt
对比新文件
@@ -0,0 +1,6 @@
package com.example.firstapp.model
data class StationGroup(
    val stationName: String,
    val count: Int
)
app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt
@@ -17,13 +17,17 @@
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.example.firstapp.R
import com.example.firstapp.activity.ContentDetailActivity
import com.example.firstapp.activity.PickupActivity
import com.example.firstapp.adapter.ExpressAdapter
import com.example.firstapp.adapter.FinanceAdapter
import com.example.firstapp.adapter.CategorySelectorAdapter
import com.example.firstapp.adapter.IncomeAdapter
import com.example.firstapp.database.service.RetrofitClient
import com.example.firstapp.databinding.FragmentHomeBinding
import com.example.firstapp.databinding.DialogCategorySelectorBinding
import com.example.firstapp.model.IncomeGroup
import com.example.firstapp.model.IncomePackage
import com.example.firstapp.utils.PreferencesManager
import com.google.android.material.bottomsheet.BottomSheetDialog
import kotlinx.coroutines.launch
@@ -39,7 +43,7 @@
    private lateinit var homeViewModel: HomeViewModel
    private lateinit var expressAdapter: ExpressAdapter
    private lateinit var financeAdapter: FinanceAdapter
    private lateinit var incomeAdapter: FinanceAdapter
    private lateinit var incomeAdapter: IncomeAdapter
    private lateinit var flightAdapter: FinanceAdapter
    private lateinit var trainAdapter: FinanceAdapter
    private lateinit var dataUpdateReceiver: BroadcastReceiver
@@ -62,12 +66,11 @@
//        val userId = getUserId() // 需要实现这个方法
        val userId ="123456"
        homeViewModel.initialize(requireContext(), userId)
        //调用这个方法来设置 RecyclerView用于设置 RecyclerView 的布局和适配器。
        setupRecyclerViews()
        // 设置点击监听事件
        setupAdapters()
        setupTabSwitching()
        //调用这个方法来观察 ViewModel 中的数据变化
        observeViewModelData()
        setupObservers()
        setupCategorySelector()
    }
@@ -85,7 +88,7 @@
        }
    }
    private fun setupRecyclerViews() {
    private fun setupAdapters() {
        binding.expressRecycler.apply {
            layoutManager = LinearLayoutManager(context)
            expressAdapter = ExpressAdapter()
@@ -97,6 +100,7 @@
                val intent = Intent(requireContext(), PickupActivity::class.java).apply {
                    putExtra("station_name", group.stationName)
                    putExtra("company", pack.company)
                    putExtra("page_type", PickupActivity.TYPE_EXPRESS)
                }
                startActivity(intent)
            }
@@ -117,6 +121,8 @@
                val intent = Intent(requireContext(), PickupActivity::class.java).apply {
                    putExtra("station_name", group.stationName)
                    putExtra("company", pack.company)
                    putExtra("page_type", PickupActivity.TYPE_REPAYMENT)
                }
                startActivity(intent)
            }
@@ -125,9 +131,22 @@
        // 添加新的 RecyclerView
        binding.incomeRecycler.apply {
            layoutManager = LinearLayoutManager(context)
            incomeAdapter = FinanceAdapter()
            incomeAdapter = IncomeAdapter()
            adapter = incomeAdapter
            visibility = View.GONE
            // 设置初始状态 - 添加这行
            binding.incomeRecycler.visibility = View.GONE
            // 设置点击监听
            incomeAdapter.setOnPackageClickListener { group, pack ->
                // 跳转到取件页面
                val intent = Intent(requireContext(), PickupActivity::class.java).apply {
                    putExtra("station_name", group.stationName)
                    putExtra("company", pack.company)
                    putExtra("page_type", PickupActivity.TYPE_INCOME)
                }
                startActivity(intent)
            }
        }
        binding.flightRecycler.apply {
@@ -151,7 +170,7 @@
            tabExpress.setTextColor(ContextCompat.getColor(requireContext(), R.color.tab_selected))
            tabFinance.setTextColor(ContextCompat.getColor(requireContext(), R.color.gray))
            // 快递标签点击事件
            // 快递标签点击事件 - 快递功能所有用户都可以使用
            tabExpress.setOnClickListener {
                hideAllRecyclers()
                expressRecycler.visibility = View.VISIBLE
@@ -159,33 +178,60 @@
                homeViewModel.loadExpressData()
            }
            
            // 财务标签点击事件
            tabFinance.setOnClickListener {
                hideAllRecyclers()
                financeRecycler.visibility = View.VISIBLE
                updateTabStyles(tabFinance)
                homeViewModel.loadFinanceData()
            }
            // 其他标签点击事件需要检查会员状态
            val memberOnlyTabs = mapOf(
                tabFinance to { homeViewModel.loadFinanceData() },
                tabIncome to { homeViewModel.loadIncomeData() },
                tabFlight to { homeViewModel.loadFlightData() },
                tabTrain to { homeViewModel.loadTrainData() }
            )
            tabIncome.setOnClickListener {
                hideAllRecyclers()
                incomeRecycler.visibility = View.VISIBLE
                updateTabStyles(tabIncome)
                homeViewModel.loadIncomeData()
            memberOnlyTabs.forEach { (tab, loadAction) ->
                tab.setOnClickListener {
                    checkMembershipAndExecute(tab) {
                        hideAllRecyclers()
                        when (tab) {
                            tabFinance -> financeRecycler.visibility = View.VISIBLE
                            tabIncome -> incomeRecycler.visibility = View.VISIBLE
                            tabFlight -> flightRecycler.visibility = View.VISIBLE
                            tabTrain -> trainRecycler.visibility = View.VISIBLE
                        }
                        updateTabStyles(tab)
                        loadAction()
                    }
                }
            }
        }
    }
            tabFlight.setOnClickListener {
                hideAllRecyclers()
                flightRecycler.visibility = View.VISIBLE
                updateTabStyles(tabFlight)
                homeViewModel.loadFlightData()
            }
    private fun checkMembershipAndExecute(tab: TextView, action: () -> Unit) {
        // 从本地获取保存的手机号
        val savedPhone = PreferencesManager.getPhone()
        if (savedPhone.isNullOrEmpty()) {
            Toast.makeText(requireContext(), "请先登录", Toast.LENGTH_SHORT).show()
            return
        }
            tabTrain.setOnClickListener {
                hideAllRecyclers()
                trainRecycler.visibility = View.VISIBLE
                updateTabStyles(tabTrain)
                homeViewModel.loadTrainData()
        // 使用协程检查会员状态
        lifecycleScope.launch {
            try {
                val response = RetrofitClient.apiService.getUserInfo(savedPhone)
                if (response.code == "0" && response.data != null) {
                    if (response.data.isMember) {
                        action()
                    } else {
                        Toast.makeText(requireContext(), "该功能仅对会员开放", Toast.LENGTH_SHORT).show()
                        // 切回快递标签
                        binding.tabExpress.performClick()
                    }
                } else {
                    Toast.makeText(requireContext(), "获取用户信息失败", Toast.LENGTH_SHORT).show()
                    binding.tabExpress.performClick()
                }
            } catch (e: Exception) {
                e.printStackTrace()
                Toast.makeText(requireContext(), "网络错误,请稍后重试", Toast.LENGTH_SHORT).show()
                binding.tabExpress.performClick()
            }
        }
    }
@@ -211,8 +257,7 @@
        }
    }
    //这个方法用于观察 homeViewModel 中的 expressItems 数据。
    private fun observeViewModelData() {
    private fun setupObservers() {
        //当 expressItems 数据发生变化时,更新 RecyclerView 的数据。
        homeViewModel.expressItems.observe(viewLifecycleOwner) { items ->
            //将新的数据列表提交给适配器,以更新 RecyclerView 的显示内容。
@@ -223,6 +268,7 @@
            financeAdapter.submitList(items)
        }
        // 观察收入数据变化
        homeViewModel.incomeItems.observe(viewLifecycleOwner) { items ->
            incomeAdapter.submitList(items)
        }
app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
@@ -14,6 +14,8 @@
import com.example.firstapp.model.ExpressPackage
import com.example.firstapp.model.FinanceGroup
import com.example.firstapp.model.FinancePackage
import com.example.firstapp.model.IncomeGroup
import com.example.firstapp.model.IncomePackage
import com.example.firstapp.util.SecureStorage
import kotlinx.coroutines.launch
@@ -21,13 +23,13 @@
    private val _expressItems = MutableLiveData<List<ExpressGroup>>()
    private val _financeItems = MutableLiveData<List<FinanceGroup>>()
    private val _incomeItems = MutableLiveData<List<FinanceGroup>>()
    private val _incomeItems = MutableLiveData<List<IncomeGroup>>()
    private val _flightItems = MutableLiveData<List<FinanceGroup>>()
    private val _trainItems = MutableLiveData<List<FinanceGroup>>()
    
    val expressItems: LiveData<List<ExpressGroup>> = _expressItems
    val financeItems: LiveData<List<FinanceGroup>> = _financeItems
    val incomeItems: LiveData<List<FinanceGroup>> = _incomeItems
    val incomeItems: LiveData<List<IncomeGroup>> = _incomeItems
    val flightItems: LiveData<List<FinanceGroup>> = _flightItems
    val trainItems: LiveData<List<FinanceGroup>> = _trainItems
@@ -56,106 +58,90 @@
        _categories.value?.let { updateVisibleCategories(it) }
    }
    fun loadExpressData() {
    private fun loadDataByType(type: String) {
        viewModelScope.launch {
            // 1. 获取所有驿站类型的提醒设置
            val stations = Core.reminder.getByType("快递")
            // 2. 按驿站分组获取包裹信息
            val groups = stations.map { station ->
                val packages = Core.code.getByKeyword(station.nickname).map { code ->
                    ExpressPackage(
                        id = code.id, //ID
                        company = code.secondLevel, //快递公司
                        trackingNumber = code.code, // 取件码
                        createTime = code.createTime  //快递时间
                    )
            try {
                // 获取该类型下的所有站点分组
                val stations = Core.code.getStationsByType(type)
                when (type) {
                    "快递" -> {
                        // 处理快递类型
                        val groups = stations.map { station ->
                            val packages = Core.code.getPackagesByTypeAndStation(type, station.stationName).map { code ->
                                ExpressPackage(
                                    id = code.id,
                                    company = code.secondLevel,
                                    trackingNumber = code.code,
                                    createTime = code.createTime
                                )
                            }
                            ExpressGroup(stationName = station.stationName, packages = packages)
                        }
                        _expressItems.postValue(groups)
                    }
                    "收入" -> {
                        // 特殊处理收入类型
                        val groups = stations.map { station ->
                            val packages = Core.code.getPackagesByTypeAndStation(type, station.stationName).map { code ->
                                IncomePackage(
                                    id = code.id,
                                    company = code.secondLevel,  // 交易对方
                                    trackingNumber = code.code,  // 金额
                                    createTime = code.createTime,
                                    balance = code.remarks.replace("余额", "") // 去掉"余额"前缀
                                )
                            }
                            IncomeGroup(stationName = station.stationName, packages = packages)
                        }
                        _incomeItems.postValue(groups)
                    }
                    else -> {
                        // 处理其他类型(还款、航班、火车票)
                        val groups = stations.map { station ->
                            val packages = Core.code.getPackagesByTypeAndStation(type, station.stationName).map { code ->
                                FinancePackage(
                                    id = code.id,
                                    company = code.secondLevel,
                                    trackingNumber = code.code,
                                    createTime = code.createTime
                                )
                            }
                            FinanceGroup(stationName = station.stationName, packages = packages)
                        }
                        // 根据类型更新对应的 LiveData
                        when (type) {
                            "还款" -> _financeItems.postValue(groups)
                            "航班" -> _flightItems.postValue(groups)
                            "火车票" -> _trainItems.postValue(groups)
                        }
                    }
                }
                ExpressGroup(
                    stationName = station.nickname, packages = packages
                )
            } catch (e: Exception) {
                Log.e("HomeViewModel", "Failed to load $type data: ${e.message}")
            }
            _expressItems.postValue(groups)
        }
    }
    fun loadExpressData() {
        loadDataByType("快递")
    }
    fun loadFinanceData() {
        viewModelScope.launch {
            // 1. 获取所有驿站类型的提醒设置
            val stations = Core.reminder.getByType("还款")
            // 2. 按驿站分组获取包裹信息
            val groups = stations.map { station ->
                val packages = Core.code.getByKeyword(station.nickname).map { code ->
                    FinancePackage(
                        id = code.id, //ID
                        company = code.secondLevel, //快递公司
                        trackingNumber = code.code, // 取件码
                        createTime = code.createTime  //快递时间
                    )
                }
                FinanceGroup(
                    stationName = station.nickname, packages = packages
                )
            }
            _financeItems.postValue(groups)
        }
        loadDataByType("还款")
    }
    fun loadIncomeData() {
        viewModelScope.launch {
            val stations = Core.reminder.getByType("收入")
            val groups = stations.map { station ->
                val packages = Core.code.getByKeyword(station.nickname).map { code ->
                    FinancePackage(
                        id = code.id,
                        company = code.secondLevel,
                        trackingNumber = code.code,
                        createTime = code.createTime
                    )
                }
                FinanceGroup(stationName = station.nickname, packages = packages)
            }
            _incomeItems.postValue(groups)
        }
        loadDataByType("收入")
    }
    fun loadFlightData() {
        viewModelScope.launch {
            val stations = Core.reminder.getByType("航班")
            val groups = stations.map { station ->
                val packages = Core.code.getByKeyword(station.nickname).map { code ->
                    FinancePackage(
                        id = code.id,
                        company = code.secondLevel,
                        trackingNumber = code.code,
                        createTime = code.createTime
                    )
                }
                FinanceGroup(stationName = station.nickname, packages = packages)
            }
            _flightItems.postValue(groups)
        }
        loadDataByType("航班")
    }
    fun loadTrainData() {
        viewModelScope.launch {
            val stations = Core.reminder.getByType("火车票")
            val groups = stations.map { station ->
                val packages = Core.code.getByKeyword(station.nickname).map { code ->
                    FinancePackage(
                        id = code.id,
                        company = code.secondLevel,
                        trackingNumber = code.code,
                        createTime = code.createTime
                    )
                }
                FinanceGroup(stationName = station.nickname, packages = packages)
            }
            _trainItems.postValue(groups)
        }
        loadDataByType("火车票")
    }
    fun loadCategories() {
app/src/main/res/layout/activity_phone_login.xml
@@ -43,7 +43,7 @@
            android:layout_height="wrap_content"
            android:background="@null"
            android:hint="请输入手机号"
            android:text="17712345678"
            android:text="15288241342"
            android:inputType="phone"
            android:maxLength="11"
            android:textSize="16sp"
app/src/main/res/layout/fragment_home.xml
@@ -119,8 +119,7 @@
                    android:id="@+id/income_recycler"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:padding="8dp"
                    android:visibility="gone" />
                    android:padding="8dp" />
                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/flight_recycler"
app/src/main/res/layout/item_income_group.xml
对比新文件
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginHorizontal="12dp"
    android:layout_marginVertical="6dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="2dp"
    app:cardBackgroundColor="@android:color/white"
    app:strokeColor="#FF000000"
    app:strokeWidth="2dp">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginBottom="12dp">
            <ImageView
                android:id="@+id/iv_station_icon"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_gravity="center_vertical"
                android:src="@drawable/location"/>
            <TextView
                android:id="@+id/tv_station_name"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_marginStart="8dp"
                android:textSize="16sp"
                android:textColor="#333333"/>
            <TextView
                android:id="@+id/tv_package_count"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="14sp"
                android:textColor="#666666"/>
        </LinearLayout>
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_packages"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</com.google.android.material.card.MaterialCardView>
app/src/main/res/layout/item_income_package_home.xml
对比新文件
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="12dp"
    android:gravity="center_vertical">
    <ImageView
        android:id="@+id/iv_company_logo"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_marginEnd="6dp"/>
    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_company"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:textColor="#333333"
            android:textStyle="bold"/>
        <TextView
            android:id="@+id/tv_create_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="10sp"
            android:textColor="#666666"
            android:layout_marginTop="4dp"/>
    </LinearLayout>
    <TextView
        android:id="@+id/tv_tracking_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="25sp"
        android:textColor="#333333"
        android:layout_marginStart="12dp"
        android:textStyle="bold"/>
</LinearLayout>