tj
9 天以前 d166fa86711d9348ac987ba87715e986a3b1a27b
app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
@@ -1,28 +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 com.google.android.material.tabs.TabLayout
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import android.graphics.Color
import android.widget.GridLayout
import com.example.firstapp.model.DailyStat
import java.util.*
class DashboardFragment : Fragment() {
@@ -33,11 +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,
@@ -48,8 +70,22 @@
        return binding.root
    }
    override fun onResume() {
        super.onResume()
        // 重新加载用户信息
        loadUserInfo()
    }
    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()
@@ -60,7 +96,31 @@
        setupView(view)
        updateDateDisplay()
        loadPackages()
        // 遮罩层
        loadUserInfo()
        // 遮罩层点击时间
        binding.overlayContent.setOnClickListener {
            // 跳转到vipActivity
            val intent = android.content.Intent(requireContext(), com.example.firstapp.activity.VipActivity::class.java)
            startActivity(intent)
        }
    }
    private fun showOverlay() {
        binding.viewOverlay.visibility = View.VISIBLE
        binding.overlayContent.visibility = View.VISIBLE
    }
    private fun hiddleOverlay() {
        binding.viewOverlay.visibility = View.GONE
        binding.overlayContent.visibility = View.GONE
    }
    private fun setupRecyclerView() {
        binding.recyclerPackages.apply {
@@ -70,8 +130,14 @@
    }
    private fun setupTabLayout() {
        val weekStatsView = binding.layoutWeekStats.root
        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
@@ -79,6 +145,40 @@
                    3 -> DateType.YEAR
                    else -> DateType.DAY
                }
                when (currentDateType) {
                    DateType.DAY -> {
                        binding.cardPackageStatsTitleText.text = "本日收到包裹总数"
                        binding.layoutYearStatsTitleText.text = "包裹取件码记录"
                        binding.cardPackageStatsTitleText.visibility = View.VISIBLE
                        binding.layoutYearStatsTitleText.visibility =  View.VISIBLE
                    }
                    DateType.WEEK -> {
                        binding.cardPackageStatsTitleText.text = "本周收到包裹总数"
                        bar_title.text = "本周收到包裹数分布 ->"
                        pie_title.text = "本周包裹物流公司分布 ->"
                        binding.cardPackageStatsTitleText.visibility = View.VISIBLE
                        binding.layoutYearStatsTitleText.visibility =  View.GONE
                    }
                    DateType.MONTH -> {
                        binding.cardPackageStatsTitleText.text = "本月收到包裹总数"
                        bar_title.text = "本月收到包裹数分布 ->"
                        pie_title.text = "本月包裹物流公司分布 ->"
                        binding.cardPackageStatsTitleText.visibility = View.VISIBLE
                        binding.layoutYearStatsTitleText.visibility =  View.GONE
                    }
                    DateType.YEAR -> {
                        bar_title.text = "本年收到包裹数分布 ->"
                        pie_title.text = "本年包裹物流公司分布 ->"
                        binding.cardPackageStatsTitleText.visibility = View.GONE
                        binding.layoutYearStatsTitleText.visibility =  View.GONE
                    }
                }
                updateDateDisplay()
                updateCharts()
                loadPackages()
@@ -111,16 +211,94 @@
    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 -> {
                // 获取本周的起始和结束日期
                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 -> "yyyy年MM月"
@@ -135,10 +313,16 @@
        }
    }
    private fun setupView(view: View) {
        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)
        barChart.setViewPortOffsets(100f, 100f, 100f, 200f)
//        barChart.invalidate()
        
        // 初始化时隐藏统计视图
        weekStatsView.visibility = View.GONE
@@ -147,6 +331,7 @@
        setupPieChart()
        setupHeatmap()
        updateCharts()
    }
    private fun setupBarChart() {
        barChart.apply {
@@ -163,7 +348,8 @@
                setDrawGridLines(false)
                granularity = 1f
                labelRotationAngle = 0f
                textSize = 12f
                textSize = 10f  //标签字体
                setExtraLeftOffset(5f)  // 减少左侧留白
                setExtraBottomOffset(15f)
            }
@@ -189,12 +375,15 @@
            setExtraOffsets(10f, 10f, 10f, 20f)
        }
        updateBarChartData()
    }
    private fun updateBarChartData() {
        val statsFlow = when (currentDateType) {
            DateType.WEEK -> {
                viewModel.getWeeklyStats(currentDate.timeInMillis, 6)
//                viewModel.getWeeklyStats(currentDate.timeInMillis, 6)
                viewModel.getWeeklyStatsChart(startDateCur,endDateCur)
            }
            DateType.MONTH -> {
                viewModel.getYearMonthlyStats(currentDate.timeInMillis)
@@ -338,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
@@ -370,14 +728,26 @@
            when (currentDateType) {
                DateType.DAY -> {
                    binding.textPackageCount.text = "${packages.size}个"
                    // 获取本周统计
                    viewModel.getPackagesReaded(currentDate.timeInMillis,
                        currentDateType.name)
                        .observe(viewLifecycleOwner) { unpackages->
                            // 只读取未取件的包裹
                            packageAdapter.updatePackages(unpackages)
                        }
                }
                DateType.WEEK -> {
                    // 获取本周统计
                    viewModel.getCurrentWeekStats(currentDate.timeInMillis)
                        .observe(viewLifecycleOwner) { stats ->
                            val weekTotal = stats.sumOf { it.count }
                            binding.textPackageCount.text = "${weekTotal}个"
                        }
                    viewModel.getCurrentWeekStats2(startDateCur,endDateCur).observe(viewLifecycleOwner) { stats ->
                        binding.textPackageCount.text = "${stats}个"
                    }
//                    viewModel.getCurrentWeekStats(currentDate.timeInMillis)
//                        .observe(viewLifecycleOwner) { stats ->
//                            val weekTotal = stats.sumOf { it.count }
//                            binding.textPackageCount.text = "${weekTotal}个"
//                        }
                }
                DateType.MONTH -> {
                    // 获取本月统计
@@ -396,8 +766,8 @@
                        }
                }
            }
            packageAdapter.updatePackages(packages)
            packageAdapter.updatePackages(packages)
//            packageAdapter.updatePackages(packages)
//            binding.textPackageCount.text = "${packages.size}个"
        }
    }
@@ -405,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
@@ -487,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")
@@ -512,6 +1414,7 @@
                binding.layoutWeekStats.root.visibility = View.VISIBLE
                binding.layoutYearStats.root.visibility = View.GONE
                binding.layoutWeekStats.chartDailyPackages.visibility = View.VISIBLE
                (binding.layoutWeekStats.chartDailyPackages.parent as? View)?.visibility = View.VISIBLE
                binding.layoutWeekStats.heatmapYearly.visibility = View.GONE
                binding.cardPackageStats.visibility = View.VISIBLE
                updateBarChartData()
@@ -537,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
@@ -549,4 +1452,35 @@
        super.onDestroyView()
        _binding = null
    }
    private fun loadUserInfo() {
        lifecycleScope.launch {
            try {
                // 从本地获取保存的手机号
                val savedPhone = PreferencesManager.getPhone()
                if (savedPhone.isNullOrEmpty()) {
                    Toast.makeText(context, "用户未登录", Toast.LENGTH_SHORT).show()
                    return@launch
                }
                val response = RetrofitClient.apiService.getUserInfo(savedPhone)
                if (response.code == "0" && response.data != null) {
                    // 保存用户信息
                    currentUserInfo = response.data
                    val userInfo = response.data
                    if(userInfo.isMember){
                        hiddleOverlay()
                    }else{
                        // 显示遮罩层
                        showOverlay()
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
                Toast.makeText(context, "获取用户信息失败", Toast.LENGTH_SHORT).show()
            }
        }
    }
}