From acb5be78c07b8499d0a38515b05a4982207c1c9a Mon Sep 17 00:00:00 2001
From: zhujie <leon.zhu@cloudroam.com.cn>
Date: 星期五, 18 四月 2025 15:24:16 +0800
Subject: [PATCH] Merge branch 'master' of http://47.96.225.205:8888/r/FirstApp2

---
 app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt |  867 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 832 insertions(+), 35 deletions(-)

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 f8808f2..5aa4f28 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,34 +1,40 @@
 package com.example.firstapp.ui.dashboard
 
-import com.example.firstapp.R
+import android.graphics.Color
 import android.os.Bundle
+import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.webkit.WebView
+import android.widget.GridLayout
+import android.widget.HorizontalScrollView
+import android.widget.LinearLayout
 import android.widget.TextView
+import android.widget.Toast
+import androidx.cardview.widget.CardView
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.viewModels
-import com.example.firstapp.databinding.FragmentDashboardBinding
-import com.google.android.material.tabs.TabLayout
+import androidx.lifecycle.lifecycleScope
 import androidx.recyclerview.widget.LinearLayoutManager
+import com.example.firstapp.R
 import com.example.firstapp.adapter.PackageAdapter
+import com.example.firstapp.database.response.UserInfo
+import com.example.firstapp.database.service.RetrofitClient
+import com.example.firstapp.databinding.FragmentDashboardBinding
+import com.example.firstapp.model.DailyStat
+import com.example.firstapp.utils.PreferencesManager
 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.ValueFormatter
-import java.util.*
-import java.text.SimpleDateFormat
-import android.graphics.Color
-import android.widget.GridLayout
-import android.widget.Toast
-import androidx.lifecycle.lifecycleScope
-import com.example.firstapp.database.response.UserInfo
-import com.example.firstapp.database.service.RetrofitClient
-import com.example.firstapp.model.DailyStat
-import com.example.firstapp.utils.PreferencesManager
+import com.google.android.material.tabs.TabLayout
 import kotlinx.coroutines.launch
+import java.text.SimpleDateFormat
+import java.util.*
+
 
 class DashboardFragment : Fragment() {
 
@@ -39,17 +45,21 @@
     private var currentDateType = DateType.DAY
     private lateinit var barChart: BarChart
     private lateinit var pieChart: PieChart
+    private lateinit var chartCourierDistriBution:CardView
     private lateinit var heatmapView: View
     private var currentUserInfo: UserInfo? = null // 确保使用你的实际数据类
 
     private var startDateCur:String = ""
     private var endDateCur:String = ""
+    private lateinit var bar_title:TextView
+    private lateinit var pie_title:TextView
 
 
     enum class DateType {
         DAY, WEEK, MONTH, YEAR
     }
     private val viewModel: DashboardViewModel by viewModels()
+
 
     override fun onCreateView(
         inflater: LayoutInflater,
@@ -68,6 +78,14 @@
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
+
+//        val webView: WebView = binding.layoutWeekStats.webView
+//        val webSettings = webView.settings
+//        webSettings.javaScriptEnabled = true // 启用 JavaScript
+//
+//
+//        // 加载本地的 HTML 文件
+//        webView.loadUrl("file:///android_asset/calendar-heatmap.html")
 
         //渲染包裹列表
         setupRecyclerView()
@@ -113,11 +131,13 @@
 
     private fun setupTabLayout() {
         val weekStatsView = binding.layoutWeekStats.root
-        val bar_title:TextView = weekStatsView.findViewById(R.id.bar_title)
-        val pie_title:TextView = weekStatsView.findViewById(R.id.pie_title)
+        bar_title = weekStatsView.findViewById(R.id.bar_title)
+        pie_title = weekStatsView.findViewById(R.id.pie_title)
 
         binding.tabDateRange.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
             override fun onTabSelected(tab: TabLayout.Tab?) {
+                currentDate = Calendar.getInstance()
+
                 currentDateType = when(tab?.position) {
                     0 -> DateType.DAY
                     1 -> DateType.WEEK
@@ -191,6 +211,81 @@
 
     private fun updateDateDisplay() {
         val dateFormat = when (currentDateType) {
+            DateType.DAY -> {
+                // 获取当天的起始和结束时间
+                val calendar = currentDate.clone() as Calendar
+                // 设置为当天的 00:00:00
+                calendar.set(Calendar.HOUR_OF_DAY, 0)
+                calendar.set(Calendar.MINUTE, 0)
+                calendar.set(Calendar.SECOND, 0)
+                calendar.set(Calendar.MILLISECOND, 0)
+                startDateCur = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(calendar.time)
+
+                // 设置为当天的 23:59:59
+                calendar.set(Calendar.HOUR_OF_DAY, 23)
+                calendar.set(Calendar.MINUTE, 59)
+                calendar.set(Calendar.SECOND, 59)
+                calendar.set(Calendar.MILLISECOND, 999)
+                endDateCur = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(calendar.time)
+
+                // 返回当天的日期格式
+                SimpleDateFormat("yyyy年MM月dd日", Locale.getDefault()).format(currentDate.time)
+            }
+            DateType.WEEK -> {
+                // 获取本周的起始和结束日期
+                val calendar = currentDate.clone() as Calendar
+                calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY)
+                val startDate = SimpleDateFormat("MM月dd日", Locale.getDefault()).format(calendar.time)
+                startDateCur = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(calendar.time)
+
+                calendar.add(Calendar.DAY_OF_WEEK, 6)
+                val endDate = SimpleDateFormat("MM月dd日", Locale.getDefault()).format(calendar.time)
+                endDateCur = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(calendar.time)
+
+                "$startDate-$endDate"
+            }
+            DateType.MONTH -> {
+                // 获取本月的起始和结束日期
+                val calendar = currentDate.clone() as Calendar
+                calendar.set(Calendar.DAY_OF_MONTH, 1)
+                startDateCur = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(calendar.time)
+
+                calendar.add(Calendar.MONTH, 1)
+                calendar.set(Calendar.DAY_OF_MONTH, 0)
+                endDateCur = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(calendar.time)
+
+                "yyyy年MM月"
+            }
+            DateType.YEAR -> {
+                // 获取当前年的起始和结束日期
+                val calendar = currentDate.clone() as Calendar
+
+                // 设置为当前年份的 1 月 1 日
+                calendar.set(Calendar.MONTH, Calendar.JANUARY)
+                calendar.set(Calendar.DAY_OF_MONTH, 1)
+                startDateCur = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(calendar.time)
+
+                // 设置为当前年份的 12 月 31 日
+                calendar.set(Calendar.MONTH, Calendar.DECEMBER)
+                calendar.set(Calendar.DAY_OF_MONTH, 31)
+                endDateCur = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(calendar.time)
+
+
+                "yyyy年"
+            }
+        }
+
+        // 更新界面显示
+        if (currentDateType == DateType.WEEK) {
+            binding.textCurrentDate.text = dateFormat
+        } else {
+            binding.textCurrentDate.text = SimpleDateFormat(dateFormat, Locale.getDefault())
+                .format(currentDate.time)
+        }
+    }
+
+    private fun updateDateDisplay_bak() {
+        val dateFormat = when (currentDateType) {
             DateType.DAY -> "yyyy年MM月dd日"
             DateType.WEEK -> {
                 // 获取本周的起始和结束日期
@@ -221,6 +316,7 @@
 
         val weekStatsView = binding.layoutWeekStats.root
         barChart = weekStatsView.findViewById(R.id.chart_daily_packages)
+        chartCourierDistriBution=weekStatsView.findViewById(R.id.chart_courier_card_view)
         pieChart = weekStatsView.findViewById(R.id.chart_courier_distribution)
         heatmapView = weekStatsView.findViewById(R.id.heatmap_yearly)
 
@@ -431,31 +527,200 @@
                 PieEntry(stat.count.toFloat(), "${stat.courierName}(${stat.count})")
             }
 
-            val dataSet = PieDataSet(entries, "快递公司分布")
-            dataSet.colors = listOf(
-                resources.getColor(R.color.purple_500),
-                resources.getColor(R.color.teal_200),
-                resources.getColor(R.color.purple_200),
-                resources.getColor(R.color.teal_700)
-            )
-            dataSet.valueTextSize = 14f // 增大数值文字大小
+            if (entries.isNotEmpty()) {
+                val dataSet = PieDataSet(entries, "快递公司分布")
+                dataSet.colors = listOf(
+                    resources.getColor(R.color.light_blue_600_1),
+                    resources.getColor(R.color.sunflower),
+                    resources.getColor(R.color.light_blue),
+                    resources.getColor(R.color.vermillion),
+                    resources.getColor(R.color.fish_belly_white),
+                    resources.getColor(R.color.light_green),
+                    resources.getColor(R.color.crimson),
+                    resources.getColor(R.color.sky_blue),
+                    resources.getColor(R.color.gold),
+                    resources.getColor(R.color.light_purple),
+                    resources.getColor(R.color.yellow),
+                    resources.getColor(R.color.canary_yellow),
+                    resources.getColor(R.color.red_purple),
+                    resources.getColor(R.color.light_cyan),
+                    resources.getColor(R.color.orange),
+                    resources.getColor(R.color.magenta),
+                    resources.getColor(R.color.light_purple_2),
+                    resources.getColor(R.color.bright_yellow),
+                    resources.getColor(R.color.emerald_green),
+                    resources.getColor(R.color.turmeric),
+                    resources.getColor(R.color.red_gold),
+                    resources.getColor(R.color.off_white),
+                    resources.getColor(R.color.tangerine),
+                    resources.getColor(R.color.aqua_blue),
+                    resources.getColor(R.color.frost),
+                    resources.getColor(R.color.wisteria),
+                    resources.getColor(R.color.cyan)
+                )
+                dataSet.valueTextSize = 14f // 增大数值文字大小
 
-            val pieData = PieData(dataSet)
-            pieData.setValueFormatter(object : ValueFormatter() {
-                override fun getFormattedValue(value: Float): String {
-                    return value.toInt().toString()
-                }
-            })
+                val pieData = PieData(dataSet)
+                pieData.setValueFormatter(object : ValueFormatter() {
+                    override fun getFormattedValue(value: Float): String {
+                        return value.toInt().toString()
+                    }
+                })
 
-            pieChart.data = pieData
-            pieChart.invalidate()
+                pieChart.data = pieData
+                pieChart.invalidate()
+
+                pieChart.visibility = View.VISIBLE  // 例如:隐藏 PieChart
+
+                pie_title.visibility = View.VISIBLE
+
+                chartCourierDistriBution.visibility =View.VISIBLE
+
+            } else {
+                // 如果 entries 为空,可以选择隐藏图表或设置一个默认显示
+                pieChart.visibility = View.GONE  // 例如:隐藏 PieChart
+
+                pie_title.visibility = View.GONE
+
+                chartCourierDistriBution.visibility =View.GONE
+
+            }
+
+
+//            val dataSet = PieDataSet(entries, "快递公司分布")
+//            dataSet.colors = listOf(
+//                resources.getColor(R.color.purple_500),
+//                resources.getColor(R.color.teal_200),
+//                resources.getColor(R.color.purple_200),
+//                resources.getColor(R.color.teal_700)
+//            )
+//            dataSet.valueTextSize = 14f // 增大数值文字大小
+//
+//            val pieData = PieData(dataSet)
+//            pieData.setValueFormatter(object : ValueFormatter() {
+//                override fun getFormattedValue(value: Float): String {
+//                    return value.toInt().toString()
+//                }
+//            })
+//
+//            pieChart.data = pieData
+//            pieChart.invalidate()
         }
     }
     private fun getDayLabels(): Array<String> {
         return arrayOf("周一", "周二", "周三", "周四", "周五", "周六", "周日")
     }
 
-    private fun loadPackages() {
+    private fun loadPackages(){
+
+
+
+//            根据日、周、月、年 获取统计数字
+        when (currentDateType) {
+            DateType.DAY -> {
+
+                viewModel.getCurrentDayStatsByType(startDateCur,endDateCur,"快递") .observe(viewLifecycleOwner) { stats ->
+                    binding.textPackageCount.text = "${stats}个"
+                }
+
+                // 获取取件记录
+                viewModel.getPackagesReaded(currentDate.timeInMillis,
+                    currentDateType.name)
+                    .observe(viewLifecycleOwner) { unpackages->
+                        // 只读取未取件的包裹
+                        packageAdapter.updatePackages(unpackages)
+                    }
+
+
+            }
+            DateType.WEEK -> {
+
+                viewModel.getCurrentDayStatsByType(startDateCur,endDateCur,"快递") .observe(viewLifecycleOwner) { stats ->
+                    binding.textPackageCount.text = "${stats}个"
+                }
+            }
+            DateType.MONTH -> {
+
+                viewModel.getCurrentDayStatsByType(startDateCur,endDateCur,"快递") .observe(viewLifecycleOwner) { stats ->
+                    binding.textPackageCount.text = "${stats}个"
+                }
+            }
+            DateType.YEAR -> {
+
+                viewModel.getCurrentDayStatsByType(startDateCur,endDateCur,"快递") .observe(viewLifecycleOwner) { stats ->
+                    binding.layoutYearStats.textTotalPackages.text = "${stats}个"
+                }
+            }
+        }
+    }
+
+    private fun loadPackages_bak2(){
+
+        val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
+
+//            根据日、周、月、年 获取统计数字
+        when (currentDateType) {
+            DateType.DAY -> {
+                val today = Calendar.getInstance()
+                val tmpCurDateStart = formatter.format(currentDate.time)
+                val tmpCurDateEnd = formatter.format(currentDate.time)
+                viewModel.getCurrentDayStatsByType(tmpCurDateStart,tmpCurDateEnd,"快递") .observe(viewLifecycleOwner) { stats ->
+                    binding.textPackageCount.text = "${stats}个"
+                }
+
+                // 获取本周统计
+                viewModel.getPackagesReaded(currentDate.timeInMillis,
+                    currentDateType.name)
+                    .observe(viewLifecycleOwner) { unpackages->
+                        // 只读取未取件的包裹
+                        packageAdapter.updatePackages(unpackages)
+                    }
+
+
+            }
+            DateType.WEEK -> {
+                val today = Calendar.getInstance()
+
+                // 获取本周的周一(第一天)
+                today.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY)
+                val firstDayOfWeek = formatter.format(today.time)
+                // 获取本周的周日(最后一天)
+                today.add(Calendar.DATE, 6)  // 加6天
+                val lastDayOfWeek = formatter.format(today.time)
+                viewModel.getCurrentDayStatsByType(firstDayOfWeek,lastDayOfWeek,"快递") .observe(viewLifecycleOwner) { stats ->
+                    binding.textPackageCount.text = "${stats}个"
+                }
+            }
+            DateType.MONTH -> {
+                val today = Calendar.getInstance()
+                today.set(Calendar.DAY_OF_MONTH, 1)  // 设置为本月第一天
+                val firstDayOfMonth = formatter.format(today.time)
+
+                today.add(Calendar.MONTH, 1)  // 移动到下个月
+                today.set(Calendar.DAY_OF_MONTH, 0)  // 设置为下个月的最后一天
+                val lastDayOfMonth = formatter.format(today.time)
+                viewModel.getCurrentDayStatsByType(firstDayOfMonth,lastDayOfMonth,"快递") .observe(viewLifecycleOwner) { stats ->
+                    binding.textPackageCount.text = "${stats}个"
+                }
+            }
+            DateType.YEAR -> {
+                val today = Calendar.getInstance()
+                today.set(Calendar.MONTH, Calendar.JANUARY)  // 设置为第一月
+                today.set(Calendar.DAY_OF_MONTH, 1)  // 设置为第一天
+                val firstDayOfYear = formatter.format(today.time)
+
+                today.add(Calendar.YEAR, 1)  // 移动到下一年
+                today.set(Calendar.MONTH, Calendar.DECEMBER)  // 设置为最后一月
+                today.set(Calendar.DAY_OF_MONTH, 31)  // 设置为最后一天
+                val lastDayOfYear = formatter.format(today.time)
+                viewModel.getCurrentDayStatsByType(firstDayOfYear,lastDayOfYear,"快递") .observe(viewLifecycleOwner) { stats ->
+//                    binding.textPackageCount.text = "${stats}个"
+                    binding.layoutYearStats.textTotalPackages.text = "${stats}个"
+                }
+            }
+        }
+    }
+    private fun loadPackages_bak() {
         viewModel.getPackages(
             currentDate.timeInMillis,
             currentDateType.name
@@ -465,7 +730,7 @@
                     binding.textPackageCount.text = "${packages.size}个"
 
                     // 获取本周统计
-                    viewModel.getPackagesUnread(currentDate.timeInMillis,
+                    viewModel.getPackagesReaded(currentDate.timeInMillis,
                         currentDateType.name)
                         .observe(viewLifecycleOwner) { unpackages->
                             // 只读取未取件的包裹
@@ -510,7 +775,539 @@
         heatmapView.visibility = View.GONE
     }
 
+
+
+    // 用于创建图例
+    private fun createLegend(): LinearLayout {
+        val legendLayout = LinearLayout(context).apply {
+            orientation = LinearLayout.HORIZONTAL
+            gravity = Gravity.END or Gravity.CENTER_VERTICAL
+            setPadding(8, 16, 8, 0)
+        }
+
+        val labelLow = TextView(context).apply {
+            text = "少"
+            textSize = 10f
+            setPadding(0, 0, 8, 0)
+        }
+        legendLayout.addView(labelLow)
+
+        val legendLevels = listOf(0, 5, 10, 15, 20)
+        legendLevels.forEach { level ->
+            val colorBox = View(context).apply {
+                setBackgroundColor(getHeatmapColor(level))
+                val size = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                layoutParams = LinearLayout.LayoutParams(size, size).apply {
+                    marginEnd = 4
+                }
+            }
+
+            val label = TextView(context).apply {
+                textSize = 10f
+                setPadding(0, 0, 8, 0)
+            }
+
+            legendLayout.addView(colorBox)
+            legendLayout.addView(label)
+        }
+
+        val labelHigh = TextView(context).apply {
+            text = "多"
+            textSize = 10f
+            setPadding(8, 0, 0, 0)
+        }
+        legendLayout.addView(labelHigh)
+
+        return legendLayout
+    }
+
     private fun updateHeatmapData() {
+        viewModel.getYearlyHeatmap(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
+            if (stats.isEmpty()) return@observe
+
+            val heatmapMatrix = Array(7) { IntArray(52) }
+            stats.forEach { stat ->
+                val week = stat.weekOfYear - 1
+                val dayOfWeek = stat.dayOfWeek - 1
+                if (week in 0..51 && dayOfWeek in 0..6) {
+                    heatmapMatrix[dayOfWeek][week] = stat.count
+                }
+            }
+
+            binding.layoutWeekStats.heatmapYearly.apply {
+                removeAllViews()
+
+                // 外层父布局:水平 LinearLayout,左固定周标签,右为横向滚动热力图
+                val outerLayout = LinearLayout(context).apply {
+                    orientation = LinearLayout.HORIZONTAL
+                }
+
+                // 左侧星期标签列
+                val dayLabelLayout = LinearLayout(context).apply {
+                    orientation = LinearLayout.VERTICAL
+                    val dayLabels = arrayOf("周一", "周二", "周三", "周四", "周五", "周六", "周日")
+                    // 顶部空白占位
+                    addView(TextView(context).apply {
+                        text = ""
+                        textSize = 20f
+                        height = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                    })
+                    dayLabels.forEach { label ->
+                        val textView = TextView(context).apply {
+                            text = label
+                            textSize = 10f
+                            height = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                            setPadding(4, 0, 4, 0)
+                        }
+                        addView(textView)
+                    }
+                }
+
+                // 右侧滚动部分
+                val scrollView = HorizontalScrollView(context).apply {
+                    isHorizontalScrollBarEnabled = false
+                    layoutParams = LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                    )
+                }
+
+                val scrollContentLayout = LinearLayout(context).apply {
+                    orientation = LinearLayout.VERTICAL
+                }
+
+                val gridLayout = GridLayout(context).apply {
+                    rowCount = 8
+                    columnCount = 52
+                }
+
+                // 添加月份标签(估算)
+                val months = arrayOf(
+                    "1月", "2月", "3月", "4月", "5月", "6月",
+                    "7月", "8月", "9月", "10月", "11月", "12月"
+                )
+               /* months.forEachIndexed { index, month ->
+                    val label = TextView(context).apply {
+                        text = month
+                        textSize = 10f
+                        setPadding(10, 0, 8, 4)
+                        val weekPosition = (index * 4.3).toInt()
+                        layoutParams = GridLayout.LayoutParams().apply {
+                            columnSpec = GridLayout.spec(weekPosition)
+                            rowSpec = GridLayout.spec(0)
+                        }
+                    }
+                    gridLayout.addView(label)
+                }*/
+                months.forEachIndexed { index, month ->
+                    val label = TextView(context).apply {
+                        text = month
+                        textSize = 10f
+                        setPadding(10, 0, 8, 4)
+
+                        val weekPosition = (index * 4.3).toInt()
+                        val span = 4  // 控制跨列范围
+                        layoutParams = GridLayout.LayoutParams().apply {
+                            columnSpec = GridLayout.spec(weekPosition, span, GridLayout.CENTER)
+                            rowSpec = GridLayout.spec(0)
+                        }
+                    }
+                    gridLayout.addView(label)
+                }
+
+                // 添加热力格子
+                for (day in 0..6) {
+                    for (week in 0..51) {
+                        val count = heatmapMatrix[day][week]
+                        val cell = View(context).apply {
+                            layoutParams = GridLayout.LayoutParams().apply {
+                                width = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                                height = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                                columnSpec = GridLayout.spec(week)
+                                rowSpec = GridLayout.spec(day + 1)
+                                setMargins(1, 1, 1, 1)
+                            }
+                            setBackgroundColor(getHeatmapColor(count))
+                        }
+                        gridLayout.addView(cell)
+                    }
+                }
+
+                scrollContentLayout.addView(gridLayout)
+                scrollView.addView(scrollContentLayout)
+
+                // 添加两个主要部分到外层布局
+                outerLayout.addView(dayLabelLayout)
+                outerLayout.addView(scrollView)
+
+                // 图例
+                val legendLayout = LinearLayout(context).apply {
+                    orientation = LinearLayout.HORIZONTAL
+                    gravity = Gravity.END or Gravity.CENTER_VERTICAL
+                    setPadding(8, 16, 8, 0)
+                    layoutParams = LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                    )
+                }
+
+                val labelLow = TextView(context).apply {
+                    text = "少"
+                    textSize = 10f
+                    setPadding(0, 0, 8, 0)
+                }
+                legendLayout.addView(labelLow)
+
+                val legendLevels = listOf(0, 5, 10, 15, 20)
+                legendLevels.forEach { level ->
+                    val colorBox = View(context).apply {
+                        setBackgroundColor(getHeatmapColor(level))
+                        val size = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                        layoutParams = LinearLayout.LayoutParams(size, size).apply {
+                            marginEnd = 4
+                        }
+                    }
+
+                    val label = TextView(context).apply {
+                        textSize = 10f
+                        setPadding(0, 0, 8, 0)
+                    }
+
+                    legendLayout.addView(colorBox)
+                    legendLayout.addView(label)
+                }
+
+                val labelHigh = TextView(context).apply {
+                    text = "多"
+                    textSize = 10f
+                    setPadding(8, 0, 0, 0)
+                }
+                legendLayout.addView(labelHigh)
+
+                // 总容器垂直布局:热力图 + 图例
+               val container = LinearLayout(context).apply {
+                    orientation = LinearLayout.VERTICAL
+                    addView(outerLayout)
+                    addView(legendLayout)
+                }
+
+//                addView(container)
+
+                // 设置外部 margin
+                val params = LinearLayout.LayoutParams(
+                    LinearLayout.LayoutParams.MATCH_PARENT,
+                    LinearLayout.LayoutParams.WRAP_CONTENT
+                ).apply {
+                    setMargins(16, 16, 16, 16)  // 左、上、右、下的 margin,单位是像素
+                }
+
+                // 添加到父布局并应用 margin
+                addView(container, params)
+
+            }
+        }
+    }
+
+
+
+    private fun updateHeatmapData_a() {
+        viewModel.getYearlyHeatmap(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
+            if (stats.isEmpty()) return@observe
+
+            // 创建52周x7天的数据矩阵
+            val heatmapMatrix = Array(7) { IntArray(52) }
+
+            // 填充数据
+            stats.forEach { stat ->
+                val week = stat.weekOfYear - 1 // 0-51
+                val dayOfWeek = stat.dayOfWeek - 1 // 0-6
+                if (week in 0..51 && dayOfWeek in 0..6) {
+                    heatmapMatrix[dayOfWeek][week] = stat.count
+                }
+            }
+
+            // 更新UI
+            binding.layoutWeekStats.heatmapYearly.apply {
+                removeAllViews()
+
+                val gridLayout = GridLayout(context).apply {
+                    rowCount = 8 // 1行月份 + 7行星期
+                    columnCount = 53 // 1列星期 + 52列周数
+                }
+
+                // 添加月份标签
+                val months = arrayOf(
+                    "1月", "2月", "3月", "4月", "5月", "6月",
+                    "7月", "8月", "9月", "10月", "11月", "12月"
+                )
+                months.forEachIndexed { index, month ->
+                    val label = TextView(context).apply {
+                        text = month
+                        textSize = 10f
+                        setPadding(0, 0, 8, 4)
+                        val weekPosition = (index * 4.3).toInt() // 估算月份起始位置
+                        layoutParams = GridLayout.LayoutParams().apply {
+                            columnSpec = GridLayout.spec(weekPosition + 1)
+                            rowSpec = GridLayout.spec(0)
+                        }
+                    }
+                    gridLayout.addView(label)
+                }
+
+                // 添加星期标签
+                val dayLabels = arrayOf("周一", "周二", "周三", "周四", "周五", "周六", "周日")
+                dayLabels.forEachIndexed { index, label ->
+                    val textView = TextView(context).apply {
+                        text = label
+                        textSize = 10f
+                        setPadding(4, 0, 8, 0)
+                        layoutParams = GridLayout.LayoutParams().apply {
+                            columnSpec = GridLayout.spec(0)
+                            rowSpec = GridLayout.spec(index + 1)
+                        }
+                    }
+                    gridLayout.addView(textView)
+                }
+
+                // 添加热力图单元格
+                for (day in 0..6) {
+                    for (week in 0..51) {
+                        val count = heatmapMatrix[day][week]
+                        val cell = View(context).apply {
+                            layoutParams = GridLayout.LayoutParams().apply {
+                                width = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                                height = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                                columnSpec = GridLayout.spec(week + 1)
+                                rowSpec = GridLayout.spec(day + 1)
+                                setMargins(1, 1, 1, 1)
+                            }
+                            setBackgroundColor(getHeatmapColor(count))
+                        }
+                        gridLayout.addView(cell)
+                    }
+                }
+
+                // 创建图例(Legend)
+                val legendLayout = LinearLayout(context).apply {
+                    orientation = LinearLayout.HORIZONTAL
+                    gravity = Gravity.END or Gravity.CENTER_VERTICAL // 靠右对齐
+                    setPadding(8, 16, 8, 0)
+                    layoutParams = LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                    )
+                }
+
+                // 左侧“少”标签
+                val labelLow = TextView(context).apply {
+                    text = "少"
+                    textSize = 10f
+                    setPadding(0, 0, 8, 0)
+                }
+                legendLayout.addView(labelLow)
+
+                // 渐变色块 + 数值
+                val legendLevels = listOf(0, 5, 10, 15, 20)
+                legendLevels.forEach { level ->
+                    val colorBox = View(context).apply {
+                        setBackgroundColor(getHeatmapColor(level))
+                        val size = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                        layoutParams = LinearLayout.LayoutParams(size, size).apply {
+                            marginEnd = 4
+                        }
+                    }
+
+                    val label = TextView(context).apply {
+//                        text = "$level"
+                        textSize = 10f
+                        setPadding(0, 0, 8, 0)
+                    }
+
+                    legendLayout.addView(colorBox)
+                    legendLayout.addView(label)
+                }
+
+                // 右侧“多”标签
+                val labelHigh = TextView(context).apply {
+                    text = "多"
+                    textSize = 10f
+                    setPadding(8, 0, 0, 0)
+                }
+                legendLayout.addView(labelHigh)
+
+                // 垂直组合 gridLayout + legendLayout
+                val container = LinearLayout(context).apply {
+                    orientation = LinearLayout.VERTICAL
+                    addView(gridLayout)
+                    addView(legendLayout)
+                }
+
+                addView(container)
+            }
+        }
+    }
+
+    private fun updateHeatmapData_aaa() {
+        viewModel.getYearlyHeatmap(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
+            if (stats.isEmpty()) return@observe
+
+            // 创建52周x7天的数据矩阵
+            val heatmapMatrix = Array(7) { IntArray(52) }
+
+            // 填充数据
+            stats.forEach { stat ->
+                val week = stat.weekOfYear - 1 // 0-51
+                val dayOfWeek = stat.dayOfWeek - 1 // 0-6
+                if (week in 0..51 && dayOfWeek in 0..6) {
+                    heatmapMatrix[dayOfWeek][week] = stat.count
+                }
+            }
+
+            // 更新UI
+            binding.layoutWeekStats.heatmapYearly.apply {
+                removeAllViews()
+
+                val gridLayout = GridLayout(context).apply {
+                    rowCount = 8 // 1行月份 + 7行星期
+                    columnCount = 53 // 1列星期 + 52列周数
+                }
+
+                // 添加月份标签
+                val months = arrayOf("1月", "2月", "3月", "4月", "5月", "6月",
+                    "7月", "8月", "9月", "10月", "11月", "12月")
+                months.forEachIndexed { index, month ->
+                    val label = TextView(context).apply {
+                        text = month
+                        textSize = 10f
+                        setPadding(0, 0, 8, 4)
+                        val weekPosition = (index * 4.3).toInt() // 估算月份起始位置
+                        layoutParams = GridLayout.LayoutParams().apply {
+                            columnSpec = GridLayout.spec(weekPosition + 1)
+                            rowSpec = GridLayout.spec(0)
+                        }
+                    }
+                    gridLayout.addView(label)
+                }
+
+                // 添加星期标签
+                val dayLabels = arrayOf("周一", "周二", "周三", "周四", "周五", "周六", "周日")
+                dayLabels.forEachIndexed { index, label ->
+                    val textView = TextView(context).apply {
+                        text = label
+                        textSize = 10f
+                        setPadding(4, 0, 8, 0)
+                        layoutParams = GridLayout.LayoutParams().apply {
+                            columnSpec = GridLayout.spec(0)
+                            rowSpec = GridLayout.spec(index + 1)
+                        }
+                    }
+                    gridLayout.addView(textView)
+                }
+
+                // 添加热力图单元格
+                for (day in 0..6) {
+                    for (week in 0..51) {
+                        val count = heatmapMatrix[day][week]
+                        val cell = View(context).apply {
+                            layoutParams = GridLayout.LayoutParams().apply {
+                                width = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                                height = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                                columnSpec = GridLayout.spec(week + 1)
+                                rowSpec = GridLayout.spec(day + 1)
+                                setMargins(1, 1, 1, 1)
+                            }
+                            setBackgroundColor(getHeatmapColor(count))
+                        }
+                        gridLayout.addView(cell)
+                    }
+                }
+
+                // ✅ 把 gridLayout 包裹进 HorizontalScrollView
+                val scrollView = HorizontalScrollView(context).apply {
+                    layoutParams = LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                    )
+                    isHorizontalScrollBarEnabled = true
+                }
+                scrollView.addView(gridLayout)
+
+
+
+
+                // 创建图例(Legend)
+                val legendLayout = LinearLayout(context).apply {
+                    orientation = LinearLayout.HORIZONTAL
+                    gravity = Gravity.END or Gravity.CENTER_VERTICAL // 靠右对齐
+                    setPadding(8, 16, 8, 0)
+                    layoutParams = LinearLayout.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT
+                    )
+                }
+
+                // 左侧“少”标签
+                val labelLow = TextView(context).apply {
+                    text = "少"
+                    textSize = 10f
+                    setPadding(0, 0, 8, 0)
+                }
+                legendLayout.addView(labelLow)
+
+                // 渐变色块 + 数值
+                val legendLevels = listOf(0, 5, 10, 15, 20)
+                legendLevels.forEach { level ->
+                    val colorBox = View(context).apply {
+                        setBackgroundColor(getHeatmapColor(level))
+                        val size = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+                        layoutParams = LinearLayout.LayoutParams(size, size).apply {
+                            marginEnd = 4
+                        }
+                    }
+
+                    val label = TextView(context).apply {
+//                        text = "$level"
+                        textSize = 10f
+                        setPadding(0, 0, 8, 0)
+                    }
+
+                    legendLayout.addView(colorBox)
+                    legendLayout.addView(label)
+                }
+
+                // 右侧“多”标签
+                val labelHigh = TextView(context).apply {
+                    text = "多"
+                    textSize = 10f
+                    setPadding(8, 0, 0, 0)
+                }
+                legendLayout.addView(labelHigh)
+
+                // 垂直组合 gridLayout + legendLayout
+                val container = LinearLayout(context).apply {
+                    orientation = LinearLayout.VERTICAL
+                    addView(scrollView)
+                    addView(legendLayout)
+                }
+
+                addView(container)
+            }
+        }
+    }
+
+    private fun getHeatmapColor(count: Int): Int {
+        return when {
+            count == 0 -> Color.parseColor("#EBECF1") // 最淡
+            count < 5 -> Color.parseColor("#ACE7B1")
+            count < 10 -> Color.parseColor("#68C16F")
+            count < 15 -> Color.parseColor("#529F57")
+            else -> Color.parseColor("#356D40") // 最深
+        }
+    }
+
+
+
+    private fun updateHeatmapData_bak() {
         viewModel.getYearlyHeatmap(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
             if (stats.isEmpty()) return@observe
 
@@ -592,7 +1389,7 @@
         }
     }
 
-    private fun getHeatmapColor(count: Int): Int {
+    private fun getHeatmapColor_bak(count: Int): Int {
         return when {
             count == 0 -> Color.parseColor("#EBEDF0")
             count == 1 -> Color.parseColor("#9BE9A8")
@@ -643,7 +1440,7 @@
             if (stats.isEmpty()) return@observe
             
             // 更新年度包裹总数
-            binding.layoutYearStats.textTotalPackages.text = "${stats.sumOf { it.count }}个"
+//            binding.layoutYearStats.textTotalPackages.text = "${stats.sumOf { it.count }}个"
             
             // 更新平均每天包裹数
             val avgDaily = stats.sumOf { it.count }.toFloat() / 365

--
Gitblit v1.9.3