From ca8bc638ba9cbca3f5f6a4d497d45f92e70064f3 Mon Sep 17 00:00:00 2001
From: cloudroam <cloudroam>
Date: 星期三, 16 四月 2025 08:44:15 +0800
Subject: [PATCH] add: 火车票和航班处理

---
 app/src/main/java/com/example/firstapp/activity/PickupActivity.kt              |    8 
 app/src/main/res/layout/activity_phone_login.xml                               |    2 
 app/src/main/res/layout/item_train_package_home.xml                            |   47 ++
 app/src/main/res/layout/item_flight.xml                                        |   59 +++
 app/src/main/java/com/example/firstapp/database/response/SmsProcessResponse.kt |    5 
 app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt                 |   46 ++
 app/src/main/java/com/example/firstapp/model/TrainGroup.kt                     |   13 
 app/src/main/res/layout/item_train_group.xml                                   |   58 +++
 app/src/main/java/com/example/firstapp/model/FlightGroup.kt                    |   13 
 app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt                |   46 ++
 app/src/main/java/com/example/firstapp/adapter/TrainAdapter.kt                 |  173 +++++++++
 app/src/main/res/layout/item_flight_package_home.xml                           |   47 ++
 app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt                 |   32 +
 app/src/main/res/layout/item_train.xml                                         |   59 +++
 app/src/main/java/com/example/firstapp/MainActivity.kt                         |  192 ++++++++--
 app/src/main/java/com/example/firstapp/adapter/FlightAdapter.kt                |  173 +++++++++
 app/src/main/res/layout/item_flight_group.xml                                  |   58 +++
 17 files changed, 978 insertions(+), 53 deletions(-)

diff --git a/app/src/main/java/com/example/firstapp/MainActivity.kt b/app/src/main/java/com/example/firstapp/MainActivity.kt
index 9c533a8..2e0691f 100644
--- a/app/src/main/java/com/example/firstapp/MainActivity.kt
+++ b/app/src/main/java/com/example/firstapp/MainActivity.kt
@@ -44,8 +44,7 @@
         registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
             when {
                 permissions.getOrDefault(
-                    Manifest.permission.RECEIVE_SMS,
-                    false
+                    Manifest.permission.RECEIVE_SMS, false
                 ) && permissions.getOrDefault(Manifest.permission.READ_SMS, false) -> {
                     // 两个权限都获得授权
                     registerSmsReceiver()
@@ -77,7 +76,7 @@
 
         // 重置提醒计划并检查是否有错过的提醒
         resetReminders()
-        
+
         // 检查权限
         if (ContextCompat.checkSelfPermission(
                 this, Manifest.permission.RECEIVE_SMS
@@ -174,7 +173,7 @@
                 while (cursor.moveToNext()) {
                     val messageBody = cursor.getString(cursor.getColumnIndexOrThrow("body"))
                     val datetime = cursor.getLong(cursor.getColumnIndexOrThrow("date"))
-                    
+
                     // 转换为 Date 对象
                     val date = Date(datetime)
                     val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
@@ -193,19 +192,21 @@
                     // 使用协程处理API调用和数据库操作
                     CoroutineScope(Dispatchers.IO).launch {
                         try {
-                            val response = RetrofitModelClient.modelService.processSms(mapOf("content" to messageBody))
-                            
+                            val response =
+                                RetrofitModelClient.modelService.processSms(mapOf("content" to messageBody))
+
                             synchronized(syncLock) {
                                 if (response.status == "success") {
                                     when (response.data.category) {
                                         "快递" -> {
                                             val pickupCode = response.data.details.pickupCode ?: ""
                                             if (pickupCode.isNotEmpty()) {
-                                                val existingCode = Core.code.queryByTypeAndCodeAndDate(
-                                                    response.data.category,
-                                                    pickupCode,
-                                                    dateString
-                                                )
+                                                val existingCode =
+                                                    Core.code.queryByTypeAndCodeAndDate(
+                                                        response.data.category,
+                                                        pickupCode,
+                                                        dateString
+                                                    )
 
                                                 if (existingCode == null) {
                                                     val code = Code(
@@ -217,32 +218,40 @@
                                                         msgId = msgId,
                                                         createTime = dateString,
                                                         oneLevel = response.data.details.post ?: "",
-                                                        secondLevel = response.data.details.company ?: "",
+                                                        secondLevel = response.data.details.company
+                                                            ?: "",
                                                         code = pickupCode,
                                                         pickup = 0,
                                                         pickupTime = "",
                                                         overTime = "",
-                                                        address = response.data.details.address ?: "",
+                                                        address = response.data.details.address
+                                                            ?: "",
                                                         remarks = response.data.details.time ?: "",
                                                     )
 //                                                    if(code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) {
-                                                    if(code.oneLevel.isNotEmpty()  && code.code.isNotEmpty()) {
+                                                    if (code.oneLevel.isNotEmpty() && code.code.isNotEmpty()) {
                                                         Core.code.insert(code)
-                                                        android.util.Log.d("MainActivity", "历史快递短信已保存: $pickupCode")
+                                                        android.util.Log.d(
+                                                            "MainActivity",
+                                                            "历史快递短信已保存: $pickupCode"
+                                                        )
                                                     }
                                                 } else {
-                                                    android.util.Log.d("MainActivity", "发现重复快递短信,跳过保存: $pickupCode")
+                                                    android.util.Log.d(
+                                                        "MainActivity",
+                                                        "发现重复快递短信,跳过保存: $pickupCode"
+                                                    )
                                                 }
                                             }
                                         }
+
                                         "还款" -> {
                                             val amount = response.data.details.amount ?: ""
                                             if (amount.isNotEmpty()) {
-                                                val existingCode = Core.code.queryByTypeAndCodeAndDate(
-                                                    response.data.category,
-                                                    amount,
-                                                    dateString
-                                                )
+                                                val existingCode =
+                                                    Core.code.queryByTypeAndCodeAndDate(
+                                                        response.data.category, amount, dateString
+                                                    )
 
                                                 if (existingCode == null) {
                                                     val code = Code(
@@ -254,31 +263,39 @@
                                                         msgId = msgId,
                                                         createTime = dateString,
                                                         oneLevel = response.data.details.type ?: "",
-                                                        secondLevel = response.data.details.bank ?: "",
+                                                        secondLevel = response.data.details.bank
+                                                            ?: "",
                                                         code = amount,
                                                         pickup = 0,
                                                         pickupTime = "",
                                                         overTime = response.data.details.date ?: "",
-                                                        address = response.data.details.address ?: "",
+                                                        address = response.data.details.address
+                                                            ?: "",
                                                         remarks = "最小还款金额${response.data.details.min_amount}还款卡号${response.data.details.number}"
                                                     )
-                                                    if(code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) {
+                                                    if (code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) {
                                                         Core.code.insert(code)
-                                                        android.util.Log.d("MainActivity", "历史还款短信已保存: $amount")
+                                                        android.util.Log.d(
+                                                            "MainActivity",
+                                                            "历史还款短信已保存: $amount"
+                                                        )
                                                     }
                                                 } else {
-                                                    android.util.Log.d("MainActivity", "发现重复还款短信,跳过保存: $amount")
+                                                    android.util.Log.d(
+                                                        "MainActivity",
+                                                        "发现重复还款短信,跳过保存: $amount"
+                                                    )
                                                 }
                                             }
                                         }
+
                                         "收入" -> {
                                             val amount = response.data.details.amount ?: ""
                                             if (amount.isNotEmpty()) {
-                                                val existingCode = Core.code.queryByTypeAndCodeAndDate(
-                                                    response.data.category,
-                                                    amount,
-                                                    dateString
-                                                )
+                                                val existingCode =
+                                                    Core.code.queryByTypeAndCodeAndDate(
+                                                        response.data.category, amount, dateString
+                                                    )
 
                                                 if (existingCode == null) {
                                                     val code = Code(
@@ -290,21 +307,114 @@
                                                         msgId = msgId,
                                                         createTime = dateString,
                                                         oneLevel = response.data.details.bank ?: "",
-                                                        secondLevel = response.data.details.bank ?: "",
+                                                        secondLevel = response.data.details.bank
+                                                            ?: "",
                                                         code = amount,
                                                         pickup = 0, // 0-未取件,1-已取件
                                                         pickupTime = "", // 取件时间为空
                                                         overTime = response.data.details.datetime
                                                             ?: "",  // 超时时间为空,暂时没有这块处理逻辑
-                                                        address = response.data.details.address ?: "",
-                                                        remarks = "余额" + response.data.details.balance ?: "",
+                                                        address = response.data.details.address
+                                                            ?: "",
+                                                        remarks = "余额" + response.data.details.balance
+                                                            ?: "",
                                                     )
-                                                    if(code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) {
+                                                    if (code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) {
                                                         Core.code.insert(code)
-                                                        android.util.Log.d("MainActivity", "历史还款短信已保存: $amount")
+                                                        android.util.Log.d(
+                                                            "MainActivity",
+                                                            "历史还款短信已保存: $amount"
+                                                        )
                                                     }
                                                 } else {
-                                                    android.util.Log.d("MainActivity", "发现重复还款短信,跳过保存: $amount")
+                                                    android.util.Log.d(
+                                                        "MainActivity",
+                                                        "发现重复还款短信,跳过保存: $amount"
+                                                    )
+                                                }
+                                            }
+                                        }
+
+                                        "航班" -> {
+                                            val flight = response.data.details.flight ?: ""
+                                            if (flight.isNotEmpty()) {
+                                                val existingCode =
+                                                    Core.code.queryByTypeAndCodeAndDate(
+                                                        response.data.category, flight, dateString
+                                                    )
+
+                                                if (existingCode == null) {
+                                                    val code = Code(
+                                                        id = 0,
+                                                        category = response.data.category,
+                                                        categoryId = 4, // 4-航班类型
+                                                        typeId = 1,     //暂时没有根据type分类
+                                                        ruleId = 2,     //1-还款类型
+                                                        msgId = msgId,
+                                                        createTime = dateString,
+                                                        oneLevel = response.data.details.company
+                                                            ?: "",
+                                                        secondLevel = response.data.details.start + response.data.details.end
+                                                            ?: "",
+                                                        code = flight,
+                                                        pickup = 0, // 0-未取件,1-已取件
+                                                        pickupTime = "", // 取件时间为空
+                                                        overTime = response.data.details.time
+                                                            ?: "",  // 超时时间为空,暂时没有这块处理逻辑
+                                                        address = response.data.details.address
+                                                            ?: "",
+                                                        remarks = response.data.details.seat ?: "",
+                                                    )
+                                                    if (code.oneLevel != "" && code.secondLevel != "" && code.code != "") {
+                                                        Core.code.insert(code)
+                                                    }
+                                                } else {
+                                                    android.util.Log.d(
+                                                        "MainActivity",
+                                                        "发现重复还款短信,跳过保存: $flight"
+                                                    )
+                                                }
+                                            }
+                                        }
+
+                                        "火车票" -> {
+                                            val seat = response.data.details.seat ?: ""
+                                            if (seat.isNotEmpty()) {
+                                                val existingCode =
+                                                    Core.code.queryByTypeAndCodeAndDate(
+                                                        response.data.category, seat, dateString
+                                                    )
+
+                                                if (existingCode == null) {
+                                                    val code = Code(
+                                                        id = 0,
+                                                        category = response.data.category,
+                                                        categoryId = 5, // 5-火车票类型
+                                                        typeId = 1,     //暂时没有根据type分类
+                                                        ruleId = 2,     //1-还款类型
+                                                        msgId = msgId,
+                                                        createTime = dateString,
+                                                        oneLevel = response.data.details.company
+                                                            ?: "",
+                                                        secondLevel = response.data.details.company
+                                                            ?: "",
+                                                        code = seat,
+                                                        pickup = 0, // 0-未取件,1-已取件
+                                                        pickupTime = "", // 取件时间为空
+                                                        overTime = response.data.details.time
+                                                            ?: "",  // 超时时间为空,暂时没有这块处理逻辑
+                                                        address = response.data.details.address
+                                                            ?: "",
+                                                        remarks = response.data.details.trips ?: "",
+                                                    )
+                                                    if (code.oneLevel != "" && code.secondLevel != "" && code.code != "") {
+                                                        Core.code.insert(code)
+                                                    }
+                                                } else {
+                                                    android.util.Log.d(
+                                                        "MainActivity",
+                                                        "发现重复还款短信,跳过保存: $seat"
+                                                    )
                                                 }
                                             }
                                         }
@@ -332,16 +442,16 @@
         try {
             // 取消所有现有的提醒任务
             androidx.work.WorkManager.getInstance(this).cancelAllWorkByTag("reminder_worker")
-            
+
             // 清除旧的提醒偏好设置
             getSharedPreferences("reminder_prefs", Context.MODE_PRIVATE).edit().clear().apply()
-            
+
             // 重新设置提醒任务
             (application as App).setupReminderWorker()
-            
+
             // 检查是否有错过的提醒
             com.example.firstapp.service.ReminderWorker.checkMissedReminders(this)
-            
+
             android.util.Log.d("MainActivity", "已重置提醒计划")
         } catch (e: Exception) {
             android.util.Log.e("MainActivity", "重置提醒计划失败: ${e.message}")
diff --git a/app/src/main/java/com/example/firstapp/activity/PickupActivity.kt b/app/src/main/java/com/example/firstapp/activity/PickupActivity.kt
index 48a6dc1..8c7cac4 100644
--- a/app/src/main/java/com/example/firstapp/activity/PickupActivity.kt
+++ b/app/src/main/java/com/example/firstapp/activity/PickupActivity.kt
@@ -20,6 +20,8 @@
         const val TYPE_EXPRESS = "express"
         const val TYPE_REPAYMENT = "repayment"
         const val TYPE_INCOME = "income"
+        const val TYPE_TRAIN = "train"
+        const val TYPE_FLIGHT = "flight"
     }
     
     private var pageType = TYPE_EXPRESS
@@ -65,6 +67,8 @@
             TYPE_EXPRESS -> "是否确认取出所有包裹?"
             TYPE_REPAYMENT -> "是否确认处理所有还款?"
             TYPE_INCOME -> "是否确认处理所有收入?"
+            TYPE_TRAIN -> "是否确认处理所有火车票?"
+            TYPE_FLIGHT -> "是否确认处理所有机票?"
             else -> "是否确认处理所有项目?"
         }
     }
@@ -74,6 +78,8 @@
             TYPE_EXPRESS -> "全部取件"
             TYPE_REPAYMENT -> "全部还款"
             TYPE_INCOME -> "全部收款"
+            TYPE_TRAIN -> "全部处理票务"
+            TYPE_FLIGHT -> "全部处理机票"
             else -> "全部处理"
         }
     }
@@ -132,6 +138,8 @@
             TYPE_EXPRESS -> "共${count}个包裹"
             TYPE_REPAYMENT -> "共${count}笔还款"
             TYPE_INCOME -> "共${count}笔收入"
+            TYPE_TRAIN -> "共${count}张车票"
+            TYPE_FLIGHT -> "共${count}张机票"
             else -> "共${count}个"
         }
     }
diff --git a/app/src/main/java/com/example/firstapp/adapter/FlightAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/FlightAdapter.kt
new file mode 100644
index 0000000..2c1365f
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/adapter/FlightAdapter.kt
@@ -0,0 +1,173 @@
+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.R
+import com.example.firstapp.databinding.ItemFlightGroupBinding
+import com.example.firstapp.databinding.ItemFlightPackageHomeBinding
+import com.example.firstapp.databinding.ItemFlightBinding
+import com.example.firstapp.model.FlightGroup
+import com.example.firstapp.model.FlightPackage
+
+class FlightAdapter :
+    ListAdapter<FlightGroup, FlightAdapter.ViewHolder>(FlightGroupDiffCallback()) {
+
+    private var onPackageClickListener: (FlightGroup, FlightPackage) -> Unit = { _, _ -> }
+
+    fun setOnPackageClickListener(listener: (FlightGroup, FlightPackage) -> Unit) {
+        onPackageClickListener = listener
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val binding = ItemFlightGroupBinding.inflate(
+            LayoutInflater.from(parent.context), parent, false
+        )
+        return ViewHolder(binding)
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        val group = getItem(position)
+        holder.bind(group)
+        holder.setOnPackageClickListener(onPackageClickListener)
+    }
+
+
+    inner class ViewHolder(private val binding: ItemFlightGroupBinding) :
+        RecyclerView.ViewHolder(binding.root) {
+        private val packagesAdapter = FlightPackageHomeAdapter { pack ->
+            currentGroup?.let { group ->
+                onPackageClickListener(group, pack)
+            }
+        }
+        private var currentGroup: FlightGroup? = null
+
+        init {
+            binding.rvPackages.apply {
+                layoutManager = LinearLayoutManager(context)
+                adapter = packagesAdapter
+            }
+        }
+
+        fun bind(group: FlightGroup) {
+            currentGroup = group
+            binding.tvStationName.text = group.stationName
+            binding.tvPackageCount.text = "共${group.packages.size}张机票"
+            packagesAdapter.submitList(group.packages)
+        }
+
+        fun setOnPackageClickListener(listener: (FlightGroup, FlightPackage) -> Unit) {
+            // 这个方法可以移除,因为我们在构造 FlightPackageHomeAdapter 时已经处理了点击事件
+            // 或者保留这个方法但不做任何操作
+        }
+    }
+}
+
+// 首页使用的机票适配器 - 简化版本
+class FlightPackageHomeAdapter(private val onPackageClick: (FlightPackage) -> Unit) :
+    ListAdapter<FlightPackage, FlightPackageHomeAdapter.ViewHolder>(FlightPackageDiffCallback()) {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val binding = ItemFlightPackageHomeBinding.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: ItemFlightPackageHomeBinding) :
+        RecyclerView.ViewHolder(binding.root) {
+
+        init {
+            binding.root.setOnClickListener {
+                val pack = getItem(adapterPosition)
+                onPackageClick(pack)
+            }
+        }
+
+        fun bind(pack: FlightPackage) {
+            binding.tvCompany.text = pack.company
+            binding.tvCreateTime.text = pack.createTime
+            binding.tvTrackingNumber.text = pack.trackingNumber
+        }
+    }
+}
+
+// 取件页面使用的机票适配器
+class FlightPackageAdapter(private val onPackagePickup: (FlightPackage) -> Unit = { _ -> }) :
+    ListAdapter<FlightPackage, FlightPackageAdapter.ViewHolder>(FlightPackageDiffCallback()) {
+
+    private var onPackageClickListener: (FlightPackage) -> Unit = {}
+    private var stationName: String = ""
+
+    fun setStationInfo(station: String) {
+        stationName = station
+    }
+
+    fun setOnPackageClickListener(listener: (FlightPackage) -> Unit) {
+        onPackageClickListener = listener
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val binding = ItemFlightBinding.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: ItemFlightBinding) :
+        RecyclerView.ViewHolder(binding.root) {
+
+        init {
+            binding.ivPackageStatus.setOnClickListener {
+                val pack = getItem(adapterPosition)
+                onPackagePickup(pack)
+            }
+
+            binding.root.setOnClickListener(null)
+        }
+
+        fun bind(pack: FlightPackage) {
+            binding.tvPackageId.text = pack.id.toString()
+            binding.tvCompany.text = pack.company
+            binding.tvCreateTime.text = pack.createTime
+            binding.tvTrackingNumber.text = pack.trackingNumber
+            binding.ivPackageStatus.setImageResource(
+                R.drawable.circle
+            )
+        }
+    }
+}
+
+
+private class FlightGroupDiffCallback : DiffUtil.ItemCallback<FlightGroup>() {
+    override fun areItemsTheSame(oldItem: FlightGroup, newItem: FlightGroup): Boolean {
+        return oldItem.stationName == newItem.stationName
+    }
+
+    override fun areContentsTheSame(oldItem: FlightGroup, newItem: FlightGroup): Boolean {
+        return oldItem == newItem
+    }
+}
+
+private class FlightPackageDiffCallback : DiffUtil.ItemCallback<FlightPackage>() {
+    override fun areItemsTheSame(oldItem: FlightPackage, newItem: FlightPackage): Boolean {
+        return oldItem.trackingNumber == newItem.trackingNumber
+    }
+
+    override fun areContentsTheSame(oldItem: FlightPackage, newItem: FlightPackage): Boolean {
+        return oldItem == newItem
+    }
+} 
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/adapter/TrainAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/TrainAdapter.kt
new file mode 100644
index 0000000..0efd02f
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/adapter/TrainAdapter.kt
@@ -0,0 +1,173 @@
+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.R
+import com.example.firstapp.databinding.ItemTrainGroupBinding
+import com.example.firstapp.databinding.ItemTrainPackageHomeBinding
+import com.example.firstapp.databinding.ItemTrainBinding
+import com.example.firstapp.model.TrainGroup
+import com.example.firstapp.model.TrainPackage
+
+class TrainAdapter :
+    ListAdapter<TrainGroup, TrainAdapter.ViewHolder>(TrainGroupDiffCallback()) {
+
+    private var onPackageClickListener: (TrainGroup, TrainPackage) -> Unit = { _, _ -> }
+
+    fun setOnPackageClickListener(listener: (TrainGroup, TrainPackage) -> Unit) {
+        onPackageClickListener = listener
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val binding = ItemTrainGroupBinding.inflate(
+            LayoutInflater.from(parent.context), parent, false
+        )
+        return ViewHolder(binding)
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        val group = getItem(position)
+        holder.bind(group)
+        holder.setOnPackageClickListener(onPackageClickListener)
+    }
+
+
+    inner class ViewHolder(private val binding: ItemTrainGroupBinding) :
+        RecyclerView.ViewHolder(binding.root) {
+        private val packagesAdapter = TrainPackageHomeAdapter { pack ->
+            currentGroup?.let { group ->
+                onPackageClickListener(group, pack)
+            }
+        }
+        private var currentGroup: TrainGroup? = null
+
+        init {
+            binding.rvPackages.apply {
+                layoutManager = LinearLayoutManager(context)
+                adapter = packagesAdapter
+            }
+        }
+
+        fun bind(group: TrainGroup) {
+            currentGroup = group
+            binding.tvStationName.text = group.stationName
+            binding.tvPackageCount.text = "共${group.packages.size}张车票"
+            packagesAdapter.submitList(group.packages)
+        }
+
+        fun setOnPackageClickListener(listener: (TrainGroup, TrainPackage) -> Unit) {
+            // 这个方法可以移除,因为我们在构造 TrainPackageHomeAdapter 时已经处理了点击事件
+            // 或者保留这个方法但不做任何操作
+        }
+    }
+}
+
+// 首页使用的车票适配器 - 简化版本
+class TrainPackageHomeAdapter(private val onPackageClick: (TrainPackage) -> Unit) :
+    ListAdapter<TrainPackage, TrainPackageHomeAdapter.ViewHolder>(TrainPackageDiffCallback()) {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val binding = ItemTrainPackageHomeBinding.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: ItemTrainPackageHomeBinding) :
+        RecyclerView.ViewHolder(binding.root) {
+
+        init {
+            binding.root.setOnClickListener {
+                val pack = getItem(adapterPosition)
+                onPackageClick(pack)
+            }
+        }
+
+        fun bind(pack: TrainPackage) {
+            binding.tvCompany.text = pack.company
+            binding.tvCreateTime.text = pack.createTime
+            binding.tvTrackingNumber.text = pack.trackingNumber
+        }
+    }
+}
+
+// 取件页面使用的车票适配器
+class TrainPackageAdapter(private val onPackagePickup: (TrainPackage) -> Unit = { _ -> }) :
+    ListAdapter<TrainPackage, TrainPackageAdapter.ViewHolder>(TrainPackageDiffCallback()) {
+
+    private var onPackageClickListener: (TrainPackage) -> Unit = {}
+    private var stationName: String = ""
+
+    fun setStationInfo(station: String) {
+        stationName = station
+    }
+
+    fun setOnPackageClickListener(listener: (TrainPackage) -> Unit) {
+        onPackageClickListener = listener
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val binding = ItemTrainBinding.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: ItemTrainBinding) :
+        RecyclerView.ViewHolder(binding.root) {
+
+        init {
+            binding.ivPackageStatus.setOnClickListener {
+                val pack = getItem(adapterPosition)
+                onPackagePickup(pack)
+            }
+
+            binding.root.setOnClickListener(null)
+        }
+
+        fun bind(pack: TrainPackage) {
+            binding.tvPackageId.text = pack.id.toString()
+            binding.tvCompany.text = pack.company
+            binding.tvCreateTime.text = pack.createTime
+            binding.tvTrackingNumber.text = pack.trackingNumber
+            binding.ivPackageStatus.setImageResource(
+                R.drawable.circle
+            )
+        }
+    }
+}
+
+
+private class TrainGroupDiffCallback : DiffUtil.ItemCallback<TrainGroup>() {
+    override fun areItemsTheSame(oldItem: TrainGroup, newItem: TrainGroup): Boolean {
+        return oldItem.stationName == newItem.stationName
+    }
+
+    override fun areContentsTheSame(oldItem: TrainGroup, newItem: TrainGroup): Boolean {
+        return oldItem == newItem
+    }
+}
+
+private class TrainPackageDiffCallback : DiffUtil.ItemCallback<TrainPackage>() {
+    override fun areItemsTheSame(oldItem: TrainPackage, newItem: TrainPackage): Boolean {
+        return oldItem.trackingNumber == newItem.trackingNumber
+    }
+
+    override fun areContentsTheSame(oldItem: TrainPackage, newItem: TrainPackage): Boolean {
+        return oldItem == newItem
+    }
+} 
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/database/response/SmsProcessResponse.kt b/app/src/main/java/com/example/firstapp/database/response/SmsProcessResponse.kt
index 523f7aa..c5633f1 100644
--- a/app/src/main/java/com/example/firstapp/database/response/SmsProcessResponse.kt
+++ b/app/src/main/java/com/example/firstapp/database/response/SmsProcessResponse.kt
@@ -28,4 +28,9 @@
     val number: String?,
     val datetime: String?,
     val balance: String?,
+    val seat: String?,
+    val trips: String?,
+    val start: String?,
+    val end: String?,
+    val flight: String?,
 )
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/model/FlightGroup.kt b/app/src/main/java/com/example/firstapp/model/FlightGroup.kt
new file mode 100644
index 0000000..5fe244e
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/model/FlightGroup.kt
@@ -0,0 +1,13 @@
+package com.example.firstapp.model
+
+data class FlightGroup(
+    val stationName: String,
+    val packages: List<FlightPackage>
+)
+
+data class FlightPackage(
+    var id: Long,
+    val company: String,
+    val trackingNumber: String,
+    val createTime: String
+) 
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/model/TrainGroup.kt b/app/src/main/java/com/example/firstapp/model/TrainGroup.kt
new file mode 100644
index 0000000..b7f4229
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/model/TrainGroup.kt
@@ -0,0 +1,13 @@
+package com.example.firstapp.model
+
+data class TrainGroup(
+    val stationName: String,
+    val packages: List<TrainPackage>
+)
+
+data class TrainPackage(
+    var id: Long,
+    val company: String,
+    val trackingNumber: String,
+    val createTime: String
+) 
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt b/app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt
index 9a611bb..f0e2073 100644
--- a/app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt
+++ b/app/src/main/java/com/example/firstapp/receiver/SmsReceiver.kt
@@ -142,6 +142,52 @@
                                         Core.code.insert(code)
                                     }
                                 }
+                                "航班" -> {
+                                    val code = Code(
+                                        id = 0,
+                                        category = response.data.category,
+                                        categoryId = 4, // 4-航班类型
+                                        typeId = 1,     //暂时没有根据type分类
+                                        ruleId = 2,     //1-还款类型
+                                        msgId = msgId,
+                                        createTime = createtime,
+                                        oneLevel = response.data.details.company ?: "",
+                                        secondLevel = response.data.details.start +response.data.details.end?: "",
+                                        code = response.data.details.seat ?: "",
+                                        pickup = 0, // 0-未取件,1-已取件
+                                        pickupTime = "", // 取件时间为空
+                                        overTime = response.data.details.time
+                                            ?: "",  // 超时时间为空,暂时没有这块处理逻辑
+                                        address = response.data.details.address ?: "",
+                                        remarks = response.data.details.seat ?: "",
+                                    )
+                                    if(code.oneLevel!=""  && code.secondLevel!="" && code.code!="") {
+                                        Core.code.insert(code)
+                                    }
+                                }
+                                "火车票" -> {
+                                    val code = Code(
+                                        id = 0,
+                                        category = response.data.category,
+                                        categoryId = 5, // 5-火车票类型
+                                        typeId = 1,     //暂时没有根据type分类
+                                        ruleId = 2,     //1-还款类型
+                                        msgId = msgId,
+                                        createTime = createtime,
+                                        oneLevel = response.data.details.company ?: "",
+                                        secondLevel = response.data.details.company ?: "",
+                                        code = response.data.details.seat ?: "",
+                                        pickup = 0, // 0-未取件,1-已取件
+                                        pickupTime = "", // 取件时间为空
+                                        overTime = response.data.details.time
+                                            ?: "",  // 超时时间为空,暂时没有这块处理逻辑
+                                        address = response.data.details.address ?: "",
+                                        remarks = response.data.details.trips ?: "",
+                                    )
+                                    if(code.oneLevel!=""  && code.secondLevel!="" && code.code!="") {
+                                        Core.code.insert(code)
+                                    }
+                                }
                             }
 
                             // 发送广播通知数据已更新
diff --git a/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt b/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt
index edaff47..03440a8 100644
--- a/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt
+++ b/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt
@@ -22,6 +22,8 @@
 import com.example.firstapp.adapter.FinanceAdapter
 import com.example.firstapp.adapter.CategorySelectorAdapter
 import com.example.firstapp.adapter.IncomeAdapter
+import com.example.firstapp.adapter.TrainAdapter
+import com.example.firstapp.adapter.FlightAdapter
 import com.example.firstapp.database.service.RetrofitClient
 import com.example.firstapp.databinding.FragmentHomeBinding
 import com.example.firstapp.databinding.DialogCategorySelectorBinding
@@ -45,8 +47,8 @@
     private lateinit var expressAdapter: ExpressAdapter
     private lateinit var financeAdapter: FinanceAdapter
     private lateinit var incomeAdapter: IncomeAdapter
-    private lateinit var flightAdapter: FinanceAdapter
-    private lateinit var trainAdapter: FinanceAdapter
+    private lateinit var flightAdapter: FlightAdapter
+    private lateinit var trainAdapter: TrainAdapter
     private lateinit var dataUpdateReceiver: BroadcastReceiver
     private lateinit var reminderUpdateReceiver: BroadcastReceiver
     private var reminderBadge: TextView? = null
@@ -185,16 +187,38 @@
 
         binding.flightRecycler.apply {
             layoutManager = LinearLayoutManager(context)
-            flightAdapter = FinanceAdapter()
+            flightAdapter = FlightAdapter()
             adapter = flightAdapter
             visibility = View.GONE
+            
+            // 设置点击监听
+            flightAdapter.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_FLIGHT)
+                }
+                startActivity(intent)
+            }
         }
 
         binding.trainRecycler.apply {
             layoutManager = LinearLayoutManager(context)
-            trainAdapter = FinanceAdapter()
+            trainAdapter = TrainAdapter()
             adapter = trainAdapter
             visibility = View.GONE
+            
+            // 设置点击监听
+            trainAdapter.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_TRAIN)
+                }
+                startActivity(intent)
+            }
         }
     }
 
diff --git a/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt b/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
index 0601ec9..45b5b0a 100644
--- a/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
+++ b/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
@@ -21,20 +21,24 @@
 import kotlinx.coroutines.launch
 import com.example.firstapp.database.repository.ReminderRecordRepository
 import com.example.firstapp.database.entity.ReminderRecord
+import com.example.firstapp.model.TrainGroup
+import com.example.firstapp.model.TrainPackage
+import com.example.firstapp.model.FlightGroup
+import com.example.firstapp.model.FlightPackage
 
 class HomeViewModel : ViewModel() {
 
     private val _expressItems = MutableLiveData<List<ExpressGroup>>()
     private val _financeItems = MutableLiveData<List<FinanceGroup>>()
     private val _incomeItems = MutableLiveData<List<IncomeGroup>>()
-    private val _flightItems = MutableLiveData<List<FinanceGroup>>()
-    private val _trainItems = MutableLiveData<List<FinanceGroup>>()
+    private val _flightItems = MutableLiveData<List<FlightGroup>>()
+    private val _trainItems = MutableLiveData<List<TrainGroup>>()
     
     val expressItems: LiveData<List<ExpressGroup>> = _expressItems
     val financeItems: LiveData<List<FinanceGroup>> = _financeItems
     val incomeItems: LiveData<List<IncomeGroup>> = _incomeItems
-    val flightItems: LiveData<List<FinanceGroup>> = _flightItems
-    val trainItems: LiveData<List<FinanceGroup>> = _trainItems
+    val flightItems: LiveData<List<FlightGroup>> = _flightItems
+    val trainItems: LiveData<List<TrainGroup>> = _trainItems
 
     private val _categories = MutableLiveData<List<CategoryConfig>>()
     val categories: LiveData<List<CategoryConfig>> = _categories
@@ -105,8 +109,38 @@
                         }
                         _incomeItems.postValue(groups)
                     }
+                    "火车票" -> {
+                        // 处理火车票类型
+                        val groups = stations.map { station ->
+                            val packages = Core.code.getPackagesByTypeAndStation(type, station.stationName).map { code ->
+                                TrainPackage(
+                                    id = code.id,
+                                    company = code.secondLevel,
+                                    trackingNumber = code.code,
+                                    createTime = code.createTime
+                                )
+                            }
+                            TrainGroup(stationName = station.stationName, packages = packages)
+                        }
+                        _trainItems.postValue(groups)
+                    }
+                    "航班" -> {
+                        // 处理航班类型
+                        val groups = stations.map { station ->
+                            val packages = Core.code.getPackagesByTypeAndStation(type, station.stationName).map { code ->
+                                FlightPackage(
+                                    id = code.id,
+                                    company = code.secondLevel,
+                                    trackingNumber = code.code,
+                                    createTime = code.createTime
+                                )
+                            }
+                            FlightGroup(stationName = station.stationName, packages = packages)
+                        }
+                        _flightItems.postValue(groups)
+                    }
                     else -> {
-                        // 处理其他类型(还款、航班、火车票)
+                        // 处理其他类型(还款)
                         val groups = stations.map { station ->
                             val packages = Core.code.getPackagesByTypeAndStation(type, station.stationName).map { code ->
                                 FinancePackage(
@@ -122,8 +156,6 @@
                         // 根据类型更新对应的 LiveData
                         when (type) {
                             "还款" -> _financeItems.postValue(groups)
-                            "航班" -> _flightItems.postValue(groups)
-                            "火车票" -> _trainItems.postValue(groups)
                         }
                     }
                 }
diff --git a/app/src/main/res/layout/activity_phone_login.xml b/app/src/main/res/layout/activity_phone_login.xml
index 833feb5..3144a69 100644
--- a/app/src/main/res/layout/activity_phone_login.xml
+++ b/app/src/main/res/layout/activity_phone_login.xml
@@ -97,7 +97,7 @@
                 android:layout_height="wrap_content"
                 android:background="@null"
                 android:hint="邀请码"
-                android:inputType="phone"
+                android:inputType="textVisiblePassword"
                 android:maxLength="11"
                 android:textSize="16sp"
                 android:padding="12dp"/>
diff --git a/app/src/main/res/layout/item_flight.xml b/app/src/main/res/layout/item_flight.xml
new file mode 100644
index 0000000..6c4e6cb
--- /dev/null
+++ b/app/src/main/res/layout/item_flight.xml
@@ -0,0 +1,59 @@
+<?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_package_status"
+        android:layout_width="12dp"
+        android:layout_height="12dp"
+        android:src="@drawable/circle"/>
+
+    <TextView
+        android:id="@+id/tv_package_id"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone"/>
+
+    <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="12sp"
+            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> 
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_flight_group.xml b/app/src/main/res/layout/item_flight_group.xml
new file mode 100644
index 0000000..cde9c53
--- /dev/null
+++ b/app/src/main/res/layout/item_flight_group.xml
@@ -0,0 +1,58 @@
+<?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> 
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_flight_package_home.xml b/app/src/main/res/layout/item_flight_package_home.xml
new file mode 100644
index 0000000..ef12f23
--- /dev/null
+++ b/app/src/main/res/layout/item_flight_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> 
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_train.xml b/app/src/main/res/layout/item_train.xml
new file mode 100644
index 0000000..6c4e6cb
--- /dev/null
+++ b/app/src/main/res/layout/item_train.xml
@@ -0,0 +1,59 @@
+<?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_package_status"
+        android:layout_width="12dp"
+        android:layout_height="12dp"
+        android:src="@drawable/circle"/>
+
+    <TextView
+        android:id="@+id/tv_package_id"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone"/>
+
+    <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="12sp"
+            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> 
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_train_group.xml b/app/src/main/res/layout/item_train_group.xml
new file mode 100644
index 0000000..80083d8
--- /dev/null
+++ b/app/src/main/res/layout/item_train_group.xml
@@ -0,0 +1,58 @@
+<?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> 
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_train_package_home.xml b/app/src/main/res/layout/item_train_package_home.xml
new file mode 100644
index 0000000..ef12f23
--- /dev/null
+++ b/app/src/main/res/layout/item_train_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> 
\ No newline at end of file

--
Gitblit v1.9.3