From 85a2d7f51a32cd747e27ab68bc8699a54419b6c7 Mon Sep 17 00:00:00 2001
From: cloudroam <cloudroam>
Date: 星期一, 03 三月 2025 10:02:04 +0800
Subject: [PATCH] fix :包裹列表
---
app/src/main/java/com/example/firstapp/repository/PackageRepository.kt | 34 ++
app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt | 226 ++++++++++++++++
app/src/main/res/layout/layout_week_stats.xml | 63 ++++
app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt | 51 +++
app/src/main/java/com/example/firstapp/dao/PackageDao.kt | 49 +++
app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt | 38 ++
app/src/main/java/com/example/firstapp/model/DailyStat.kt | 16 +
app/src/main/res/drawable/resource_package.xml | 15 +
app/src/main/java/com/example/firstapp/model/CourierStat.kt | 15 +
app/src/main/java/com/example/firstapp/model/PackageInfo.kt | 15 +
app/src/main/java/com/example/firstapp/database/AppDatabase.kt | 45 ++
app/src/main/res/layout/fragment_dashboard.xml | 127 ++++++++
app/src/main/res/layout/item_package_dashboard.xml | 57 ++++
app/build.gradle | 3
14 files changed, 712 insertions(+), 42 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 82e8b39..75f7708 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -270,6 +270,7 @@
// 滚轮滑动控件级联
implementation 'com.contrarywind:Android-PickerView:4.1.9'
-
+ //图形化
+ implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt
new file mode 100644
index 0000000..4abce71
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt
@@ -0,0 +1,51 @@
+package com.example.firstapp.adapter
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.firstapp.R
+import com.example.firstapp.model.PackageInfo
+import java.text.SimpleDateFormat
+import java.util.Locale
+
+
+class PackageAdapter : RecyclerView.Adapter<PackageAdapter.PackageViewHolder>() {
+
+ private var packages = listOf<PackageInfo>()
+
+ fun updatePackages(newPackages: List<PackageInfo>) {
+ packages = newPackages
+ notifyDataSetChanged()
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PackageViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.item_package_dashboard, parent, false)
+ return PackageViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: PackageViewHolder, position: Int) {
+ holder.bind(packages[position])
+ }
+
+ override fun getItemCount() = packages.size
+
+ class PackageViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ private val imgCourier: ImageView = view.findViewById(R.id.img_courier)
+ private val textCourierName: TextView = view.findViewById(R.id.text_courier_name)
+ private val textTrackingNumber: TextView = view.findViewById(R.id.text_tracking_number)
+ private val textTime: TextView = view.findViewById(R.id.text_time)
+
+ fun bind(packageInfo: PackageInfo) {
+ imgCourier.setImageResource(packageInfo.courierIcon)
+ textCourierName.text = packageInfo.courierName
+ textTrackingNumber.text = packageInfo.trackingNumber
+ textTime.text = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault())
+ .format(packageInfo.receivedTime)
+ }
+ }
+}
+
diff --git a/app/src/main/java/com/example/firstapp/dao/PackageDao.kt b/app/src/main/java/com/example/firstapp/dao/PackageDao.kt
new file mode 100644
index 0000000..e156386
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/dao/PackageDao.kt
@@ -0,0 +1,49 @@
+package com.example.firstapp.dao
+
+import androidx.room.*
+import com.example.firstapp.model.PackageInfo
+import com.example.firstapp.model.CourierStat
+import com.example.firstapp.model.DailyStat
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface PackageDao {
+ @Query("SELECT * FROM packages WHERE date(receivedTime/1000, 'unixepoch', 'localtime') = date(:date/1000, 'unixepoch', 'localtime') ORDER BY receivedTime DESC")
+ fun getPackagesByDay(date: Long): Flow<List<PackageInfo>>
+
+ @Query("""
+ SELECT * FROM packages
+ WHERE strftime('%Y-%W', receivedTime/1000, 'unixepoch', 'localtime') =
+ strftime('%Y-%W', :date/1000, 'unixepoch', 'localtime')
+ ORDER BY receivedTime DESC
+ """)
+ fun getPackagesByWeek(date: Long): Flow<List<PackageInfo>>
+
+ @Query("""
+ SELECT * FROM CourierStat
+ WHERE EXISTS (
+ SELECT 1 FROM packages p
+ WHERE p.courierName = CourierStat.courierName
+ AND strftime('%Y-%W', p.receivedTime/1000, 'unixepoch', 'localtime') =
+ strftime('%Y-%W', :date/1000, 'unixepoch', 'localtime')
+ )
+ """)
+ fun getCourierStatsByWeek(date: Long): Flow<List<CourierStat>>
+
+ @Query("""
+ SELECT * FROM DailyStat
+ WHERE EXISTS (
+ SELECT 1 FROM packages p
+ WHERE date(p.receivedTime/1000, 'unixepoch', 'localtime') = DailyStat.date
+ AND strftime('%Y-%W', p.receivedTime/1000, 'unixepoch', 'localtime') =
+ strftime('%Y-%W', :date/1000, 'unixepoch', 'localtime')
+ )
+ """)
+ fun getDailyStatsByWeek(date: Long): Flow<List<DailyStat>>
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insert(packageInfo: PackageInfo)
+
+ @Update
+ fun update(packageInfo: PackageInfo)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/database/AppDatabase.kt b/app/src/main/java/com/example/firstapp/database/AppDatabase.kt
index 2251240..b520136 100644
--- a/app/src/main/java/com/example/firstapp/database/AppDatabase.kt
+++ b/app/src/main/java/com/example/firstapp/database/AppDatabase.kt
@@ -7,6 +7,7 @@
import androidx.room.TypeConverters
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
+import com.example.firstapp.dao.PackageDao
import com.example.firstapp.database.dao.CodeDao
import com.example.firstapp.database.dao.KeywordDao
import com.example.firstapp.database.dao.MsgDao
@@ -20,11 +21,23 @@
import com.example.firstapp.utils.TAG_LIST
import com.example.firstapp.database.ext.ConvertersDate
-
+import com.example.firstapp.model.PackageInfo
+import com.example.firstapp.model.CourierStat
+import com.example.firstapp.model.DailyStat
@Database(
- entities = [ Msg::class, Code::class, KeywordEntity::class, Reminder::class],
+ entities = [
+ Msg::class,
+ Code::class,
+ KeywordEntity::class,
+ Reminder::class,
+ PackageInfo::class
+ ],
+ views = [
+ CourierStat::class,
+ DailyStat::class
+ ],
version = 20,
exportSchema = false
)
@@ -34,6 +47,7 @@
abstract fun codeDao(): CodeDao
abstract fun keywordDao(): KeywordDao
abstract fun reminderDao(): ReminderDao
+ abstract fun packageDao(): PackageDao
companion object {
@Volatile
@@ -71,16 +85,6 @@
-
-
-
-
-
-
-
-
-
-
private val MIGRATION_MSG = object : Migration(19, 20) {
override fun migrate(database: SupportSQLiteDatabase) {
//database.execSQL("Create table Msg as Select id,type,`from`,content,(case when sim_info like 'SIM1%' then '0' when sim_info like 'SIM2%' then '1' else '-1' end) as sim_slot,sim_info,sub_id,time from Logs where 1 = 1")
@@ -110,6 +114,23 @@
`isEnabled` INTEGER NOT NULL
)
""")
+
+ // 创建 CourierStat 视图
+ database.execSQL("""
+ CREATE VIEW IF NOT EXISTS CourierStat AS
+ SELECT courierName, COUNT(*) as count
+ FROM packages
+ GROUP BY courierName
+ """)
+
+ // 创建 DailyStat 视图
+ database.execSQL("""
+ CREATE VIEW IF NOT EXISTS DailyStat AS
+ SELECT date(receivedTime/1000, 'unixepoch', 'localtime') as date,
+ COUNT(*) as count
+ FROM packages
+ GROUP BY date(receivedTime/1000, 'unixepoch', 'localtime')
+ """)
// database.execSQL("""
// CREATE TABLE IF NOT EXISTS `reminders` (
// id INTEGER PRIMARY KEY AUTOINCREMENT,
diff --git a/app/src/main/java/com/example/firstapp/model/CourierStat.kt b/app/src/main/java/com/example/firstapp/model/CourierStat.kt
new file mode 100644
index 0000000..c112e7c
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/model/CourierStat.kt
@@ -0,0 +1,15 @@
+package com.example.firstapp.model
+
+import androidx.room.DatabaseView
+
+@DatabaseView(
+ """
+ SELECT courierName, COUNT(*) as count
+ FROM packages
+ GROUP BY courierName
+ """
+)
+data class CourierStat(
+ val courierName: String,
+ val count: Int
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/model/DailyStat.kt b/app/src/main/java/com/example/firstapp/model/DailyStat.kt
new file mode 100644
index 0000000..dc61551
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/model/DailyStat.kt
@@ -0,0 +1,16 @@
+package com.example.firstapp.model
+
+import androidx.room.DatabaseView
+
+@DatabaseView(
+ """
+ SELECT date(receivedTime/1000, 'unixepoch', 'localtime') as date,
+ COUNT(*) as count
+ FROM packages
+ GROUP BY date(receivedTime/1000, 'unixepoch', 'localtime')
+ """
+)
+data class DailyStat(
+ val date: String,
+ val count: Int
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/model/PackageInfo.kt b/app/src/main/java/com/example/firstapp/model/PackageInfo.kt
new file mode 100644
index 0000000..d1e2005
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/model/PackageInfo.kt
@@ -0,0 +1,15 @@
+package com.example.firstapp.model
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "packages")
+data class PackageInfo(
+ @PrimaryKey
+ val trackingNumber: String, // 快递单号
+ val courierName: String, // 快递公司名称
+ val receivedTime: Long, // 收件时间
+ val courierIcon: Int, // 快递公司图标资源ID
+ val status: Int = 0 // 取件状态: 0-未取件, 1-已取件
+)
+
diff --git a/app/src/main/java/com/example/firstapp/repository/PackageRepository.kt b/app/src/main/java/com/example/firstapp/repository/PackageRepository.kt
new file mode 100644
index 0000000..082178c
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/repository/PackageRepository.kt
@@ -0,0 +1,34 @@
+package com.example.firstapp.repository
+
+import com.example.firstapp.dao.PackageDao
+import com.example.firstapp.model.PackageInfo
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+class PackageRepository(private val packageDao: PackageDao) {
+
+ fun getPackages(date: Long, dateType: String): Flow<List<PackageInfo>> {
+ return when(dateType) {
+ "DAY" -> packageDao.getPackagesByDay(date)
+ "WEEK" -> packageDao.getPackagesByWeek(date)
+ else -> packageDao.getPackagesByDay(date)
+ }
+ }
+
+ fun getCourierStats(date: Long) = packageDao.getCourierStatsByWeek(date)
+
+ fun getDailyStats(date: Long) = packageDao.getDailyStatsByWeek(date)
+
+ suspend fun insert(packageInfo: PackageInfo) {
+ withContext(Dispatchers.IO) {
+ packageDao.insert(packageInfo)
+ }
+ }
+
+ suspend fun update(packageInfo: PackageInfo) {
+ withContext(Dispatchers.IO) {
+ packageDao.update(packageInfo)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt b/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
index 0be390f..992fcb1 100644
--- a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
@@ -1,5 +1,6 @@
package com.example.firstapp.ui.dashboard
+import com.example.firstapp.R
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -7,7 +8,20 @@
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
+import androidx.fragment.app.viewModels
import com.example.firstapp.databinding.FragmentDashboardBinding
+import com.google.android.material.tabs.TabLayout
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.example.firstapp.adapter.PackageAdapter
+import com.example.firstapp.model.PackageInfo
+import com.github.mikephil.charting.charts.BarChart
+import com.github.mikephil.charting.charts.PieChart
+import com.github.mikephil.charting.components.Legend
+import com.github.mikephil.charting.components.XAxis
+import com.github.mikephil.charting.data.*
+import com.github.mikephil.charting.formatter.IndexAxisValueFormatter
+import java.util.*
+import java.text.SimpleDateFormat
class DashboardFragment : Fragment() {
@@ -17,24 +31,216 @@
// onDestroyView.
private val binding get() = _binding!!
+ private val packageAdapter = PackageAdapter()
+ private var currentDate = Calendar.getInstance()
+ private var currentDateType = DateType.DAY
+ private lateinit var barChart: BarChart
+ private lateinit var pieChart: PieChart
+ enum class DateType {
+ DAY, WEEK, MONTH, YEAR
+ }
+ private val viewModel: DashboardViewModel by viewModels()
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
- val dashboardViewModel =
- ViewModelProvider(this).get(DashboardViewModel::class.java)
-
_binding = FragmentDashboardBinding.inflate(inflater, container, false)
- val root: View = binding.root
-
- val textView: TextView = binding.textDashboard
- dashboardViewModel.text.observe(viewLifecycleOwner) {
- textView.text = it
- }
- return root
+ return binding.root
}
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ setupRecyclerView()
+ setupTabLayout()
+ setupDatePicker()
+ updateDateDisplay()
+ setupWeekView(view)
+ loadPackages()
+ }
+
+ private fun setupRecyclerView() {
+ binding.recyclerPackages.apply {
+ layoutManager = LinearLayoutManager(context)
+ adapter = packageAdapter
+ }
+ }
+
+ private fun setupTabLayout() {
+ binding.tabDateRange.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+ override fun onTabSelected(tab: TabLayout.Tab?) {
+ currentDateType = when(tab?.position) {
+ 0 -> DateType.DAY
+ 1 -> DateType.WEEK
+ 2 -> DateType.MONTH
+ 3 -> DateType.YEAR
+ else -> DateType.DAY
+ }
+ // 切换视图
+ binding.viewFlipperStats.displayedChild = when(currentDateType) {
+ DateType.DAY -> 0
+ DateType.WEEK -> 1
+ else -> 0
+ }
+ updateDateDisplay()
+// loadPackages()
+ observePackages()
+ }
+ override fun onTabUnselected(tab: TabLayout.Tab?) {}
+ override fun onTabReselected(tab: TabLayout.Tab?) {}
+ })
+ }
+
+ private fun setupDatePicker() {
+ binding.btnPreviousDate.setOnClickListener {
+ adjustDate(-1)
+ }
+ binding.btnNextDate.setOnClickListener {
+ adjustDate(1)
+ }
+ }
+
+ private fun adjustDate(amount: Int) {
+ when (currentDateType) {
+ DateType.DAY -> currentDate.add(Calendar.DAY_OF_MONTH, amount)
+ DateType.WEEK -> currentDate.add(Calendar.WEEK_OF_YEAR, amount)
+ DateType.MONTH -> currentDate.add(Calendar.MONTH, amount)
+ DateType.YEAR -> currentDate.add(Calendar.YEAR, amount)
+ }
+ updateDateDisplay()
+ loadPackages()
+ }
+
+ private fun updateDateDisplay() {
+ val dateFormat = when (currentDateType) {
+ DateType.DAY -> "yyyy年MM月dd日"
+ DateType.WEEK -> "yyyy年第ww周"
+ DateType.MONTH -> "yyyy年MM月"
+ DateType.YEAR -> "yyyy年"
+ }
+ binding.textCurrentDate.text = SimpleDateFormat(dateFormat, Locale.getDefault())
+ .format(currentDate.time)
+ }
+ private fun setupWeekView(view: View) {
+ val weekStatsView = binding.layoutWeekStats.root
+ barChart = weekStatsView.findViewById(R.id.chart_daily_packages)
+ pieChart = weekStatsView.findViewById(R.id.chart_courier_distribution)
+ setupBarChart()
+ setupPieChart()
+ }
+ private fun setupBarChart() {
+ // 配置柱状图
+ barChart.apply {
+ description.isEnabled = false
+ setDrawGridBackground(false)
+ legend.isEnabled = false
+
+ // X轴设置
+ xAxis.apply {
+ position = XAxis.XAxisPosition.BOTTOM
+ setDrawGridLines(false)
+ valueFormatter = IndexAxisValueFormatter(getDayLabels())
+ }
+
+ // Y轴设置
+ axisLeft.apply {
+ setDrawGridLines(true)
+ axisMinimum = 0f
+ }
+ axisRight.isEnabled = false
+ }
+
+ updateBarChartData()
+ }
+ private fun updateBarChartData() {
+ viewModel.getDailyStats(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
+ val entries = stats.mapIndexed { index, stat ->
+ BarEntry(index.toFloat(), stat.count.toFloat())
+ }
+
+ val dataSet = BarDataSet(entries, "包裹数量")
+ dataSet.color = resources.getColor(R.color.purple_500)
+
+ barChart.data = BarData(dataSet)
+ barChart.invalidate()
+ }
+ }
+ private fun setupPieChart() {
+ // 配置饼图
+ pieChart.apply {
+ description.isEnabled = false
+ setUsePercentValues(true)
+ setDrawEntryLabels(false)
+
+ legend.isEnabled = true
+ legend.verticalAlignment = Legend.LegendVerticalAlignment.CENTER
+ legend.horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT
+ legend.orientation = Legend.LegendOrientation.VERTICAL
+ }
+
+ updatePieChartData()
+ }
+ private fun updatePieChartData() {
+ viewModel.getCourierStats(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
+ val entries = stats.map { stat ->
+ PieEntry(stat.count.toFloat(), stat.courierName)
+ }
+
+ val dataSet = PieDataSet(entries, "快递公司分布")
+ dataSet.colors = listOf(
+ resources.getColor(R.color.purple_500),
+ resources.getColor(R.color.teal_200)
+ )
+
+ pieChart.data = PieData(dataSet)
+ pieChart.invalidate()
+ }
+ }
+ private fun getDayLabels(): Array<String> {
+ return arrayOf("周一", "周二", "周三", "周四", "周五", "周六", "周日")
+ }
+
+ private fun loadPackages() {
+ // 这里应该从数据库或网络加载数据
+ // 这里使用模拟数据作为示例
+ val mockPackages = listOf(
+ PackageInfo(
+ trackingNumber = "14-6-7023",
+ courierName = "某快递",
+ receivedTime = System.currentTimeMillis(),
+ courierIcon = R.drawable.data
+ ),
+ PackageInfo(
+ trackingNumber = "230721",
+ courierName = "京东",
+ receivedTime = System.currentTimeMillis() - 3600000,
+ courierIcon = R.drawable.data
+ )
+ )
+
+ packageAdapter.updatePackages(mockPackages)
+ binding.textPackageCount.text = "${mockPackages.size}个"
+ }
+ private fun observePackages() {
+ viewModel.getPackages(
+ currentDate.timeInMillis,
+ currentDateType.name
+ ).observe(viewLifecycleOwner) { packages ->
+ when (currentDateType) {
+ DateType.WEEK -> {
+ // 更新图表数据
+ updateBarChartData()
+ updatePieChartData()
+ }
+ else -> {
+ packageAdapter.updatePackages(packages)
+ }
+ }
+ binding.textPackageCount.text = "${packages.size}个"
+ }
+ }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
diff --git a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt b/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt
index e47a4a6..dae2211 100644
--- a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt
+++ b/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt
@@ -1,13 +1,35 @@
package com.example.firstapp.ui.dashboard
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.asLiveData
+import androidx.lifecycle.viewModelScope
+import com.example.firstapp.AppDatabase
+import com.example.firstapp.model.PackageInfo
+import com.example.firstapp.repository.PackageRepository
+import kotlinx.coroutines.launch
-class DashboardViewModel : ViewModel() {
-
- private val _text = MutableLiveData<String>().apply {
- value = "This is dashboard Fragment"
+class DashboardViewModel(application: Application) : AndroidViewModel(application) {
+
+ private val repository: PackageRepository
+
+ init {
+ val packageDao = AppDatabase.getInstance(application).packageDao()
+ repository = PackageRepository(packageDao)
}
- val text: LiveData<String> = _text
+
+ fun getPackages(date: Long, dateType: String) =
+ repository.getPackages(date, dateType).asLiveData()
+
+ fun getCourierStats(date: Long) = repository.getCourierStats(date).asLiveData()
+
+ fun getDailyStats(date: Long) = repository.getDailyStats(date).asLiveData()
+
+ fun updatePackageStatus(packageInfo: PackageInfo) = viewModelScope.launch {
+ repository.update(packageInfo)
+ }
+
+ fun insert(packageInfo: PackageInfo) = viewModelScope.launch {
+ repository.insert(packageInfo)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/resource_package.xml b/app/src/main/res/drawable/resource_package.xml
new file mode 100644
index 0000000..d41543f
--- /dev/null
+++ b/app/src/main/res/drawable/resource_package.xml
@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="48dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="48dp">
+
+ <path android:fillColor="#FFE89C" android:pathData="M787.6,387.8v157.7c0,16.6 -13.4,30 -30,30s-30,-13.4 -30,-30v-137.3l58.7,-29c0.6,1.8 0.9,3.6 1.1,5.6 0.1,1 0.2,2 0.2,3z"/>
+
+ <path android:fillColor="#FFC477" android:pathData="M1024,299.6v424.7c0,26.9 -14.9,51 -39,63l-452.4,229.2a70.6,70.6 0,0 1,-31.4 7.4h-0.4c-11.1,-0.1 -22.1,-2.7 -32.2,-8l-0.2,-0.1 -430.7,-228.6C14.5,775.1 0,751.2 0,724.9V299.1a70.4,70.4 0,0 1,8.5 -33.6,70.4 70.4,0 0,1 29.3,-28.8L468.7,8c20,-10.4 43.9,-10.6 64,-0.6l0.2,0.1 452.2,229.1c13.3,6.6 23.8,17 30.5,29.4 5.5,10.1 8.5,21.6 8.5,33.6z"/>
+
+ <path android:fillColor="#FFB655" android:pathData="M1024,299.6v424.7c0,26.9 -14.9,51 -39,63l-452.4,229.2a70.6,70.6 0,0 1,-31.4 7.4h-0.4V520.4l514.7,-254.3c5.5,10.1 8.5,21.6 8.5,33.6z"/>
+
+ <path android:fillColor="#FFD399" android:pathData="M1015.5,266l-229.2,113.3 -56.4,27.9 -229.1,113.2L8.5,265.5a70.4,70.4 0,0 1,29.3 -28.8l184.7,-98 64,-34 182.1,-96.6A70.3,70.3 0,0 1,500.8 0a70.5,70.5 0,0 1,31.8 7.4l0.2,0.1 452.2,229.1c13.3,6.6 23.8,17 30.5,29.4z"/>
+
+ <path android:fillColor="#FFFCE5" android:pathData="M787.6,387.8v157.7c0,16.6 -13.4,30 -30,30s-30,-13.4 -30,-30v-139.6l-226.8,-120 -278.3,-147.3 64,-34 214.3,113.4 270.8,143.3 0.1,0.1c0.6,0.3 1.1,0.7 1.7,1 0.3,0.2 0.6,0.4 0.9,0.6l0,0c2.3,1.6 4.4,3.5 6.1,5.5 0.1,0.1 0.2,0.2 0.2,0.3 0.4,0.5 0.8,1.1 1.2,1.6 0.2,0.3 0.4,0.5 0.6,0.8 0.3,0.4 0.5,0.8 0.8,1.2 0.3,0.4 0.5,0.9 0.8,1.3 0.1,0.3 0.3,0.5 0.4,0.8 0.3,0.6 0.6,1.3 0.9,1.9 0,0.1 0.1,0.2 0.1,0.3 0.3,0.8 0.6,1.7 0.9,2.5 0.6,1.8 0.9,3.6 1.1,5.6 0.1,1 0.2,2 0.2,3z"/>
+
+ <path android:fillColor="#B5B51F" android:pathData="M352.2,602.8c-8.4,-14.3 -26.8,-19.1 -41,-10.7l-92.9,54.5 -19.5,-35.3c-8,-14.5 -26.2,-19.8 -40.7,-11.8s-19.8,26.2 -11.8,40.7l34.4,62.3c0.1,0.1 0.2,0.3 0.3,0.4 0,0.1 0.1,0.2 0.1,0.3 0.1,0.2 0.2,0.4 0.4,0.6 0.2,0.3 0.4,0.6 0.5,0.9 0.2,0.3 0.4,0.6 0.6,0.8 0.2,0.3 0.4,0.6 0.6,0.8 0.2,0.3 0.4,0.5 0.6,0.8 0.2,0.3 0.4,0.5 0.7,0.8 0.2,0.3 0.5,0.5 0.7,0.7 0.2,0.2 0.5,0.5 0.7,0.7 0.2,0.2 0.5,0.5 0.7,0.7 0.2,0.2 0.5,0.4 0.7,0.6 0.3,0.2 0.5,0.5 0.8,0.7 0.2,0.2 0.5,0.4 0.7,0.6 0.3,0.2 0.6,0.4 0.9,0.6 0.2,0.2 0.5,0.3 0.7,0.5 0.3,0.2 0.6,0.4 0.9,0.6 0.2,0.2 0.5,0.3 0.7,0.4 0.3,0.2 0.6,0.3 1,0.5 0.3,0.1 0.5,0.3 0.8,0.4 0.3,0.2 0.7,0.3 1,0.5 0.3,0.1 0.5,0.2 0.8,0.3 0.3,0.1 0.7,0.3 1,0.4 0.3,0.1 0.5,0.2 0.8,0.3 0.3,0.1 0.7,0.2 1,0.3 0.3,0.1 0.6,0.2 0.8,0.2 0.3,0.1 0.7,0.2 1,0.2 0.3,0.1 0.6,0.1 0.9,0.2 0.3,0.1 0.6,0.1 1,0.2 0.3,0.1 0.6,0.1 1,0.2 0.3,0 0.6,0.1 0.9,0.1 0.3,0 0.7,0.1 1,0.1 0.3,0 0.5,0 0.8,0 0.4,0 0.8,0 1.1,0 0.6,0 1.3,-0 1.9,-0.1 0.2,-0 0.3,-0 0.5,-0 0.5,-0 0.9,-0.1 1.4,-0.1 0.2,-0 0.5,-0.1 0.7,-0.1 0.4,-0.1 0.8,-0.1 1.2,-0.2 0.3,-0.1 0.6,-0.1 0.8,-0.2 0.3,-0.1 0.7,-0.2 1,-0.2 0.3,-0.1 0.6,-0.2 0.9,-0.3 0.3,-0.1 0.6,-0.2 1,-0.3 0.3,-0.1 0.6,-0.2 0.9,-0.3 0.3,-0.1 0.6,-0.2 0.9,-0.4 0.3,-0.1 0.6,-0.3 0.9,-0.4 0.3,-0.1 0.6,-0.3 0.9,-0.4 0.3,-0.1 0.6,-0.3 0.9,-0.4 0.2,-0.1 0.4,-0.2 0.6,-0.3 0.1,-0 0.2,-0.1 0.2,-0.1 0.2,-0.1 0.3,-0.2 0.4,-0.2l119.4,-70.1c14.3,-8.4 19.1,-26.8 10.7,-41z"/>
+
+</vector>
diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml
index 166ab0e..4d90e0c 100644
--- a/app/src/main/res/layout/fragment_dashboard.xml
+++ b/app/src/main/res/layout/fragment_dashboard.xml
@@ -6,17 +6,122 @@
android:layout_height="match_parent"
tools:context=".ui.dashboard.DashboardFragment">
- <TextView
- android:id="@+id/text_dashboard"
+ <!-- 时间范围选择器 -->
+ <com.google.android.material.tabs.TabLayout
+ android:id="@+id/tab_date_range"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="8dp"
- android:textAlignment="center"
- android:textSize="20sp"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
+ app:layout_constraintTop_toTopOf="parent">
+
+ <com.google.android.material.tabs.TabItem
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="天" />
+
+ <com.google.android.material.tabs.TabItem
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="周" />
+
+ <com.google.android.material.tabs.TabItem
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="月" />
+
+ <com.google.android.material.tabs.TabItem
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="年" />
+ </com.google.android.material.tabs.TabLayout>
+
+ <!-- 日期选择器 -->
+ <LinearLayout
+ android:id="@+id/layout_date_picker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="16dp"
+ app:layout_constraintTop_toBottomOf="@id/tab_date_range">
+
+ <ImageButton
+ android:id="@+id/btn_previous_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground"
+ android:src="@android:drawable/ic_media_previous" />
+
+ <TextView
+ android:id="@+id/text_current_date"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:textSize="16sp"
+ tools:text="2025年1月14日" />
+
+ <ImageButton
+ android:id="@+id/btn_next_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground"
+ android:src="@android:drawable/ic_media_next" />
+ </LinearLayout>
+
+ <!-- 包裹统计卡片 -->
+ <androidx.cardview.widget.CardView
+ android:id="@+id/card_package_stats"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ app:cardCornerRadius="8dp"
+ app:cardElevation="4dp"
+ app:layout_constraintTop_toBottomOf="@id/layout_date_picker">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="16dp">
+
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:src="@drawable/resource_package" />
+
+ <TextView
+ android:id="@+id/text_package_count"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="16dp"
+ android:textSize="24sp"
+ android:textStyle="bold"
+ tools:text="4个" />
+ </LinearLayout>
+ </androidx.cardview.widget.CardView>
+
+ <!-- 包裹列表 -->
+ <ViewFlipper
+ android:id="@+id/view_flipper_stats"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toBottomOf="@id/card_package_stats">
+
+ <!-- 日视图 -->
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/recycler_packages"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <!-- 周视图 -->
+ <include
+ android:id="@+id/layout_week_stats"
+ layout="@layout/layout_week_stats"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <!-- 月视图和年视图可以根据需要添加 -->
+
+ </ViewFlipper>
+
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_package_dashboard.xml b/app/src/main/res/layout/item_package_dashboard.xml
new file mode 100644
index 0000000..b0990bb
--- /dev/null
+++ b/app/src/main/res/layout/item_package_dashboard.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="16dp"
+ android:layout_marginVertical="4dp"
+ app:cardCornerRadius="8dp"
+ app:cardElevation="2dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="12dp">
+
+ <ImageView
+ android:id="@+id/img_courier"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ tools:src="@android:drawable/ic_menu_gallery" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="12dp"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/text_courier_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:textStyle="bold"
+ tools:text="某快递" />
+
+ <TextView
+ android:id="@+id/text_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:textColor="@android:color/darker_gray"
+ android:textSize="12sp"
+ tools:text="2025-01-14 10:30" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/text_tracking_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/darker_gray"
+ tools:text="14-6-7023" />
+ </LinearLayout>
+
+</androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_week_stats.xml b/app/src/main/res/layout/layout_week_stats.xml
new file mode 100644
index 0000000..0a2d857
--- /dev/null
+++ b/app/src/main/res/layout/layout_week_stats.xml
@@ -0,0 +1,63 @@
+<?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="vertical">
+
+ <!-- 包裹总数统计卡片 -->
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="16dp">
+
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:src="@drawable/resource_package" />
+
+ <TextView
+ android:id="@+id/text_total_packages"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="16dp"
+ android:textSize="24sp"
+ android:textStyle="bold"
+ android:text="4个" />
+ </LinearLayout>
+ </androidx.cardview.widget.CardView>
+
+ <!-- 每日包裹数量柱状图 -->
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:layout_marginHorizontal="16dp"
+ android:layout_marginBottom="16dp">
+
+ <com.github.mikephil.charting.charts.BarChart
+ android:id="@+id/chart_daily_packages"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="8dp" />
+ </androidx.cardview.widget.CardView>
+
+ <!-- 快递公司占比饼图 -->
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:layout_marginHorizontal="16dp"
+ android:layout_marginBottom="16dp">
+
+ <com.github.mikephil.charting.charts.PieChart
+ android:id="@+id/chart_courier_distribution"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="8dp" />
+ </androidx.cardview.widget.CardView>
+</LinearLayout>
\ No newline at end of file
--
Gitblit v1.9.3