|
@@ -1,9 +1,9 @@
|
|
|
-import React, { useState } from 'react'
|
|
|
|
|
|
|
+import React, { useState, useEffect, memo } from 'react'
|
|
|
import { View, Text, ScrollView } from '@tarojs/components'
|
|
import { View, Text, ScrollView } from '@tarojs/components'
|
|
|
import { useQuery } from '@tanstack/react-query'
|
|
import { useQuery } from '@tanstack/react-query'
|
|
|
import { YongrenTabBarLayout } from '@d8d/yongren-shared-ui/components/YongrenTabBarLayout'
|
|
import { YongrenTabBarLayout } from '@d8d/yongren-shared-ui/components/YongrenTabBarLayout'
|
|
|
import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
|
|
import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
|
|
|
-import { enterpriseStatisticsClient } from '@/api/enterpriseStatisticsClient'
|
|
|
|
|
|
|
+import { enterpriseStatisticsClient } from '../../api/enterpriseStatisticsClient'
|
|
|
import type {
|
|
import type {
|
|
|
DisabilityTypeDistributionResponse,
|
|
DisabilityTypeDistributionResponse,
|
|
|
GenderDistributionResponse,
|
|
GenderDistributionResponse,
|
|
@@ -11,7 +11,7 @@ import type {
|
|
|
HouseholdDistributionResponse,
|
|
HouseholdDistributionResponse,
|
|
|
JobStatusDistributionResponse,
|
|
JobStatusDistributionResponse,
|
|
|
SalaryDistributionResponse
|
|
SalaryDistributionResponse
|
|
|
-} from '@/api/types'
|
|
|
|
|
|
|
+} from '../../api/types'
|
|
|
|
|
|
|
|
export interface StatisticsProps {
|
|
export interface StatisticsProps {
|
|
|
// 组件属性定义(目前为空)
|
|
// 组件属性定义(目前为空)
|
|
@@ -20,6 +20,16 @@ export interface StatisticsProps {
|
|
|
const Statistics: React.FC<StatisticsProps> = () => {
|
|
const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
// 状态:选中的残疾类型
|
|
// 状态:选中的残疾类型
|
|
|
const [selectedDisabilityType, setSelectedDisabilityType] = useState<any>(null)
|
|
const [selectedDisabilityType, setSelectedDisabilityType] = useState<any>(null)
|
|
|
|
|
+ // 状态:时间筛选(年-月)
|
|
|
|
|
+ const [timeFilter, setTimeFilter] = useState({ year: 2023, month: 11 })
|
|
|
|
|
+ // 状态:部门筛选(占位符)
|
|
|
|
|
+ const [departmentFilter, setDepartmentFilter] = useState<string>('')
|
|
|
|
|
+ // 状态:下钻省份(户籍分布)
|
|
|
|
|
+ const [drillDownProvince, setDrillDownProvince] = useState<string | null>(null)
|
|
|
|
|
+ // 状态:图表类型切换
|
|
|
|
|
+ const [chartType, setChartType] = useState<'bar' | 'pie'>('bar')
|
|
|
|
|
+ // 状态:图表懒加载控制 - 哪些图表已经加载
|
|
|
|
|
+ const [loadedCharts, setLoadedCharts] = useState<Set<string>>(new Set(['disability', 'gender'])) // 默认加载前两个关键图表
|
|
|
|
|
|
|
|
// 类型守卫:检查响应是否包含统计数据
|
|
// 类型守卫:检查响应是否包含统计数据
|
|
|
const isSuccessfulResponse = (data: any): data is { companyId: number; stats: any[]; total: number } => {
|
|
const isSuccessfulResponse = (data: any): data is { companyId: number; stats: any[]; total: number } => {
|
|
@@ -31,6 +41,22 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
return isSuccessfulResponse(data) ? data.stats : []
|
|
return isSuccessfulResponse(data) ? data.stats : []
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 图表懒加载效果:逐步加载其他图表
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const timer1 = setTimeout(() => {
|
|
|
|
|
+ setLoadedCharts(prev => new Set([...prev, 'age', 'household']))
|
|
|
|
|
+ }, 500) // 500ms后加载年龄和户籍分布
|
|
|
|
|
+
|
|
|
|
|
+ const timer2 = setTimeout(() => {
|
|
|
|
|
+ setLoadedCharts(prev => new Set([...prev, 'jobStatus', 'salary']))
|
|
|
|
|
+ }, 1000) // 1000ms后加载在职状态和薪资分布
|
|
|
|
|
+
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ clearTimeout(timer1)
|
|
|
|
|
+ clearTimeout(timer2)
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [])
|
|
|
|
|
+
|
|
|
// 处理残疾类型图表点击
|
|
// 处理残疾类型图表点击
|
|
|
const handleDisabilityTypeClick = (item: any) => {
|
|
const handleDisabilityTypeClick = (item: any) => {
|
|
|
setSelectedDisabilityType(item)
|
|
setSelectedDisabilityType(item)
|
|
@@ -38,13 +64,75 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
console.log('点击残疾类型:', item)
|
|
console.log('点击残疾类型:', item)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 处理时间筛选变化
|
|
|
|
|
+ const handleTimeFilterChange = (newYear: number, newMonth: number) => {
|
|
|
|
|
+ setTimeFilter({ year: newYear, month: newMonth })
|
|
|
|
|
+ // 注意:当前API不支持时间筛选参数,此处仅为前端交互演示
|
|
|
|
|
+ console.log('时间筛选变更:', newYear, '年', newMonth, '月')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理部门筛选变化
|
|
|
|
|
+ const handleDepartmentFilterChange = (department: string) => {
|
|
|
|
|
+ setDepartmentFilter(department)
|
|
|
|
|
+ // 注意:当前API不支持部门筛选参数,此处仅为前端交互演示
|
|
|
|
|
+ console.log('部门筛选变更:', department)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理省份点击(下钻功能)
|
|
|
|
|
+ const handleProvinceClick = (province: string) => {
|
|
|
|
|
+ setDrillDownProvince(province)
|
|
|
|
|
+ console.log('下钻省份:', province)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理返回省份列表
|
|
|
|
|
+ const handleBackToProvinces = () => {
|
|
|
|
|
+ setDrillDownProvince(null)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理图表类型切换
|
|
|
|
|
+ const handleChartTypeToggle = () => {
|
|
|
|
|
+ setChartType(chartType === 'bar' ? 'pie' : 'bar')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 模拟城市数据(下钻演示)
|
|
|
|
|
+ const getMockCityData = (province: string) => {
|
|
|
|
|
+ const mockData: Record<string, Array<{key: string, value: number, percentage: number}>> = {
|
|
|
|
|
+ '江苏省': [
|
|
|
|
|
+ { key: '南京市', value: 15, percentage: 30 },
|
|
|
|
|
+ { key: '苏州市', value: 12, percentage: 24 },
|
|
|
|
|
+ { key: '无锡市', value: 8, percentage: 16 },
|
|
|
|
|
+ { key: '常州市', value: 6, percentage: 12 },
|
|
|
|
|
+ { key: '其他', value: 9, percentage: 18 }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '浙江省': [
|
|
|
|
|
+ { key: '杭州市', value: 18, percentage: 36 },
|
|
|
|
|
+ { key: '宁波市', value: 10, percentage: 20 },
|
|
|
|
|
+ { key: '温州市', value: 7, percentage: 14 },
|
|
|
|
|
+ { key: '其他', value: 15, percentage: 30 }
|
|
|
|
|
+ ],
|
|
|
|
|
+ '广东省': [
|
|
|
|
|
+ { key: '广州市', value: 22, percentage: 44 },
|
|
|
|
|
+ { key: '深圳市', value: 18, percentage: 36 },
|
|
|
|
|
+ { key: '其他', value: 10, percentage: 20 }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ return mockData[province] || [
|
|
|
|
|
+ { key: '城市A', value: 10, percentage: 40 },
|
|
|
|
|
+ { key: '城市B', value: 8, percentage: 32 },
|
|
|
|
|
+ { key: '其他', value: 7, percentage: 28 }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 获取残疾类型分布数据
|
|
// 获取残疾类型分布数据
|
|
|
const { data: disabilityData, isLoading: isLoadingDisability } = useQuery({
|
|
const { data: disabilityData, isLoading: isLoadingDisability } = useQuery({
|
|
|
queryKey: ['statistics', 'disability-type-distribution'],
|
|
queryKey: ['statistics', 'disability-type-distribution'],
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
const response = await enterpriseStatisticsClient['disability-type-distribution'].$get({ query: {} })
|
|
const response = await enterpriseStatisticsClient['disability-type-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ enabled: loadedCharts.has('disability'),
|
|
|
|
|
+ staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
|
|
|
|
|
+ gcTime: 10 * 60 * 1000 // 缓存时间10分钟
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// 获取性别分布数据
|
|
// 获取性别分布数据
|
|
@@ -53,7 +141,10 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
const response = await enterpriseStatisticsClient['gender-distribution'].$get({ query: {} })
|
|
const response = await enterpriseStatisticsClient['gender-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ enabled: loadedCharts.has('gender'),
|
|
|
|
|
+ staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
|
|
|
|
|
+ gcTime: 10 * 60 * 1000 // 缓存时间10分钟
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// 获取年龄分布数据
|
|
// 获取年龄分布数据
|
|
@@ -62,7 +153,10 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
const response = await enterpriseStatisticsClient['age-distribution'].$get({ query: {} })
|
|
const response = await enterpriseStatisticsClient['age-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ enabled: loadedCharts.has('age'),
|
|
|
|
|
+ staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
|
|
|
|
|
+ gcTime: 10 * 60 * 1000 // 缓存时间10分钟
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// 获取户籍分布数据
|
|
// 获取户籍分布数据
|
|
@@ -71,7 +165,10 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
const response = await enterpriseStatisticsClient['household-distribution'].$get({ query: {} })
|
|
const response = await enterpriseStatisticsClient['household-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ enabled: loadedCharts.has('household'),
|
|
|
|
|
+ staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
|
|
|
|
|
+ gcTime: 10 * 60 * 1000 // 缓存时间10分钟
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// 获取在职状态分布数据
|
|
// 获取在职状态分布数据
|
|
@@ -80,7 +177,10 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
const response = await enterpriseStatisticsClient['job-status-distribution'].$get({ query: {} })
|
|
const response = await enterpriseStatisticsClient['job-status-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ enabled: loadedCharts.has('jobStatus'),
|
|
|
|
|
+ staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
|
|
|
|
|
+ gcTime: 10 * 60 * 1000 // 缓存时间10分钟
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// 获取薪资分布数据
|
|
// 获取薪资分布数据
|
|
@@ -89,7 +189,10 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
const response = await enterpriseStatisticsClient['salary-distribution'].$get({ query: {} })
|
|
const response = await enterpriseStatisticsClient['salary-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ enabled: loadedCharts.has('salary'),
|
|
|
|
|
+ staleTime: 5 * 60 * 1000, // 数据过期时间5分钟
|
|
|
|
|
+ gcTime: 10 * 60 * 1000 // 缓存时间10分钟
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const isLoading = isLoadingDisability || isLoadingGender || isLoadingAge ||
|
|
const isLoading = isLoadingDisability || isLoadingGender || isLoadingAge ||
|
|
@@ -113,12 +216,81 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
placeholder={true}
|
|
placeholder={true}
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
- {/* 时间筛选 */}
|
|
|
|
|
- <View className="flex justify-between items-center mb-4 pt-4">
|
|
|
|
|
- <Text className="font-semibold text-gray-700">数据统计</Text>
|
|
|
|
|
- <View className="flex items-center bg-gray-100 rounded-lg px-3 py-1">
|
|
|
|
|
- <Text className="text-sm text-gray-700 mr-2">2023年11月</Text>
|
|
|
|
|
- <Text className="fas fa-chevron-down text-gray-500"></Text>
|
|
|
|
|
|
|
+ {/* 数据筛选区域 */}
|
|
|
|
|
+ <View className="mb-4 pt-4">
|
|
|
|
|
+ <View className="flex justify-between items-center mb-3">
|
|
|
|
|
+ <Text className="font-semibold text-gray-700">数据统计</Text>
|
|
|
|
|
+ <View className="flex items-center space-x-2">
|
|
|
|
|
+ <View
|
|
|
|
|
+ className="flex items-center bg-blue-50 rounded-lg px-3 py-1 active:bg-blue-100"
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ // 数据导出功能演示
|
|
|
|
|
+ console.log('导出数据')
|
|
|
|
|
+ // 在实际应用中,这里可以触发数据导出
|
|
|
|
|
+ alert('数据导出功能演示:此功能需要API支持才能导出实际数据')
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text className="text-sm text-blue-600 mr-1">导出数据</Text>
|
|
|
|
|
+ <Text className="fas fa-download text-blue-500 text-xs"></Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View
|
|
|
|
|
+ className="flex items-center bg-gray-100 rounded-lg px-3 py-1 active:bg-gray-200"
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ // 简单的时间选择器交互演示
|
|
|
|
|
+ const newMonth = timeFilter.month === 11 ? 12 : 11
|
|
|
|
|
+ handleTimeFilterChange(timeFilter.year, newMonth)
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text className="text-sm text-gray-700 mr-2">{timeFilter.year}年{timeFilter.month}月</Text>
|
|
|
|
|
+ <Text className="fas fa-chevron-down text-gray-500"></Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 部门筛选器(占位符) */}
|
|
|
|
|
+ <View className="flex items-center bg-gray-50 rounded-lg px-3 py-2">
|
|
|
|
|
+ <Text className="text-sm text-gray-600 mr-3">部门筛选:</Text>
|
|
|
|
|
+ <View className="flex space-x-2">
|
|
|
|
|
+ {['全部', '技术部', '市场部', '人力资源部'].map((dept) => (
|
|
|
|
|
+ <View
|
|
|
|
|
+ key={dept}
|
|
|
|
|
+ className={`px-3 py-1 rounded-full text-sm ${departmentFilter === dept ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-700'}`}
|
|
|
|
|
+ onClick={() => handleDepartmentFilterChange(dept === '全部' ? '' : dept)}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text>{dept}</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 筛选状态提示 */}
|
|
|
|
|
+ {(departmentFilter || timeFilter.month !== 11 || timeFilter.year !== 2023) && (
|
|
|
|
|
+ <View className="mt-2 p-2 bg-blue-50 rounded-lg">
|
|
|
|
|
+ <Text className="text-xs text-blue-700">
|
|
|
|
|
+ 当前筛选:{timeFilter.year}年{timeFilter.month}月
|
|
|
|
|
+ {departmentFilter && ` | 部门:${departmentFilter}`}
|
|
|
|
|
+ (注:当前API不支持筛选参数,仅前端交互演示)
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </View>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 图表类型切换 */}
|
|
|
|
|
+ <View className="flex justify-end items-center mb-3">
|
|
|
|
|
+ <Text className="text-sm text-gray-600 mr-2">图表类型:</Text>
|
|
|
|
|
+ <View className="flex bg-gray-100 rounded-lg p-1">
|
|
|
|
|
+ <View
|
|
|
|
|
+ className={`px-3 py-1 rounded-md ${chartType === 'bar' ? 'bg-white shadow-sm' : ''}`}
|
|
|
|
|
+ onClick={() => setChartType('bar')}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text className={`text-sm ${chartType === 'bar' ? 'text-blue-600 font-medium' : 'text-gray-600'}`}>柱状图</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View
|
|
|
|
|
+ className={`px-3 py-1 rounded-md ${chartType === 'pie' ? 'bg-white shadow-sm' : ''}`}
|
|
|
|
|
+ onClick={() => setChartType('pie')}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text className={`text-sm ${chartType === 'pie' ? 'text-blue-600 font-medium' : 'text-gray-600'}`}>饼图</Text>
|
|
|
|
|
+ </View>
|
|
|
</View>
|
|
</View>
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
@@ -165,8 +337,8 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
return (
|
|
return (
|
|
|
<View
|
|
<View
|
|
|
key={item.key}
|
|
key={item.key}
|
|
|
- className="chart-bar absolute bottom-0 bg-blue-500 rounded-t-lg"
|
|
|
|
|
- style={{ left: `${left}px`, height: `${height}px`, width: '30px' }}
|
|
|
|
|
|
|
+ className="chart-bar absolute bottom-0 bg-blue-500 rounded-t-lg active:bg-blue-600 touch-manipulation"
|
|
|
|
|
+ style={{ left: `${left}px`, height: `${height}px`, width: '30px', minHeight: '8px' }}
|
|
|
onClick={() => handleDisabilityTypeClick(item)}
|
|
onClick={() => handleDisabilityTypeClick(item)}
|
|
|
/>
|
|
/>
|
|
|
)
|
|
)
|
|
@@ -209,6 +381,45 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
)}
|
|
)}
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
|
|
+ {/* 选中的残疾类型详情 */}
|
|
|
|
|
+ {selectedDisabilityType && (
|
|
|
|
|
+ <View className="card bg-blue-50 p-4 mb-4 rounded-lg border border-blue-200">
|
|
|
|
|
+ <View className="flex justify-between items-center mb-2">
|
|
|
|
|
+ <Text className="font-semibold text-blue-800">选中的残疾类型详情</Text>
|
|
|
|
|
+ <View
|
|
|
|
|
+ className="w-6 h-6 rounded-full bg-blue-100 flex items-center justify-center"
|
|
|
|
|
+ onClick={() => setSelectedDisabilityType(null)}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text className="text-blue-500 text-xs">×</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View className="grid grid-cols-2 gap-3">
|
|
|
|
|
+ <View>
|
|
|
|
|
+ <Text className="text-sm text-gray-600">残疾类型</Text>
|
|
|
|
|
+ <Text className="font-medium text-gray-800">{selectedDisabilityType.key}</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View>
|
|
|
|
|
+ <Text className="text-sm text-gray-600">人数</Text>
|
|
|
|
|
+ <Text className="font-medium text-gray-800">{selectedDisabilityType.value}人</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View>
|
|
|
|
|
+ <Text className="text-sm text-gray-600">百分比</Text>
|
|
|
|
|
+ <Text className="font-medium text-gray-800">{selectedDisabilityType.percentage}%</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View>
|
|
|
|
|
+ <Text className="text-sm text-gray-600">占比说明</Text>
|
|
|
|
|
+ <Text className="font-medium text-gray-800">
|
|
|
|
|
+ {selectedDisabilityType.percentage >= 50 ? '主要残疾类型' :
|
|
|
|
|
+ selectedDisabilityType.percentage >= 20 ? '常见残疾类型' : '少数残疾类型'}
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <Text className="text-xs text-blue-600 mt-2">
|
|
|
|
|
+ 提示:点击其他图表项可查看详情,点击×关闭
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
{/* 性别分布 */}
|
|
{/* 性别分布 */}
|
|
|
<View className="card bg-white p-4 mb-4 rounded-lg shadow-sm">
|
|
<View className="card bg-white p-4 mb-4 rounded-lg shadow-sm">
|
|
|
<Text className="font-semibold text-gray-700 mb-3">性别分布</Text>
|
|
<Text className="font-semibold text-gray-700 mb-3">性别分布</Text>
|
|
@@ -226,8 +437,8 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
<View key={item.key} className="bar-container flex flex-col items-center">
|
|
<View key={item.key} className="bar-container flex flex-col items-center">
|
|
|
<Text className="bar-value text-sm font-semibold mb-1">{item.value}人</Text>
|
|
<Text className="bar-value text-sm font-semibold mb-1">{item.value}人</Text>
|
|
|
<View
|
|
<View
|
|
|
- className="bar rounded-t-lg"
|
|
|
|
|
- style={{ height: `${height}px`, width: '40px', backgroundColor: color }}
|
|
|
|
|
|
|
+ className="bar rounded-t-lg active:opacity-80 touch-manipulation"
|
|
|
|
|
+ style={{ height: `${height}px`, width: '40px', backgroundColor: color, minHeight: '4px' }}
|
|
|
/>
|
|
/>
|
|
|
<Text className="bar-label text-xs text-gray-500 mt-2">
|
|
<Text className="bar-label text-xs text-gray-500 mt-2">
|
|
|
{item.key} ({item.percentage}%)
|
|
{item.key} ({item.percentage}%)
|
|
@@ -408,4 +619,4 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-export default Statistics
|
|
|
|
|
|
|
+export default memo(Statistics)
|