|
@@ -1,9 +1,9 @@
|
|
|
-import React from 'react'
|
|
|
|
|
|
|
+import React, { useState } 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,18 +11,38 @@ import type {
|
|
|
HouseholdDistributionResponse,
|
|
HouseholdDistributionResponse,
|
|
|
JobStatusDistributionResponse,
|
|
JobStatusDistributionResponse,
|
|
|
SalaryDistributionResponse
|
|
SalaryDistributionResponse
|
|
|
-} from '../api/types'
|
|
|
|
|
|
|
+} from '@/api/types'
|
|
|
|
|
|
|
|
export interface StatisticsProps {
|
|
export interface StatisticsProps {
|
|
|
// 组件属性定义(目前为空)
|
|
// 组件属性定义(目前为空)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const Statistics: React.FC<StatisticsProps> = () => {
|
|
const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
|
|
+ // 状态:选中的残疾类型
|
|
|
|
|
+ const [selectedDisabilityType, setSelectedDisabilityType] = useState<any>(null)
|
|
|
|
|
+
|
|
|
|
|
+ // 类型守卫:检查响应是否包含统计数据
|
|
|
|
|
+ const isSuccessfulResponse = (data: any): data is { companyId: number; stats: any[]; total: number } => {
|
|
|
|
|
+ return data && typeof data === 'object' && 'stats' in data && Array.isArray(data.stats)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取统计数据数组,如果响应不成功则返回空数组
|
|
|
|
|
+ const getStats = (data: any): any[] => {
|
|
|
|
|
+ return isSuccessfulResponse(data) ? data.stats : []
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理残疾类型图表点击
|
|
|
|
|
+ const handleDisabilityTypeClick = (item: any) => {
|
|
|
|
|
+ setSelectedDisabilityType(item)
|
|
|
|
|
+ // 在实际应用中,这里可以显示模态框或跳转到详情页
|
|
|
|
|
+ console.log('点击残疾类型:', item)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 获取残疾类型分布数据
|
|
// 获取残疾类型分布数据
|
|
|
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()
|
|
|
|
|
|
|
+ const response = await enterpriseStatisticsClient['disability-type-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
@@ -31,7 +51,7 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
const { data: genderData, isLoading: isLoadingGender } = useQuery({
|
|
const { data: genderData, isLoading: isLoadingGender } = useQuery({
|
|
|
queryKey: ['statistics', 'gender-distribution'],
|
|
queryKey: ['statistics', 'gender-distribution'],
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
- const response = await enterpriseStatisticsClient['gender-distribution'].$get()
|
|
|
|
|
|
|
+ const response = await enterpriseStatisticsClient['gender-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
@@ -40,7 +60,7 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
const { data: ageData, isLoading: isLoadingAge } = useQuery({
|
|
const { data: ageData, isLoading: isLoadingAge } = useQuery({
|
|
|
queryKey: ['statistics', 'age-distribution'],
|
|
queryKey: ['statistics', 'age-distribution'],
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
- const response = await enterpriseStatisticsClient['age-distribution'].$get()
|
|
|
|
|
|
|
+ const response = await enterpriseStatisticsClient['age-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
@@ -49,7 +69,7 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
const { data: householdData, isLoading: isLoadingHousehold } = useQuery({
|
|
const { data: householdData, isLoading: isLoadingHousehold } = useQuery({
|
|
|
queryKey: ['statistics', 'household-distribution'],
|
|
queryKey: ['statistics', 'household-distribution'],
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
- const response = await enterpriseStatisticsClient['household-distribution'].$get()
|
|
|
|
|
|
|
+ const response = await enterpriseStatisticsClient['household-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
@@ -58,7 +78,7 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
const { data: jobStatusData, isLoading: isLoadingJobStatus } = useQuery({
|
|
const { data: jobStatusData, isLoading: isLoadingJobStatus } = useQuery({
|
|
|
queryKey: ['statistics', 'job-status-distribution'],
|
|
queryKey: ['statistics', 'job-status-distribution'],
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
- const response = await enterpriseStatisticsClient['job-status-distribution'].$get()
|
|
|
|
|
|
|
+ const response = await enterpriseStatisticsClient['job-status-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
@@ -67,7 +87,7 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
const { data: salaryData, isLoading: isLoadingSalary } = useQuery({
|
|
const { data: salaryData, isLoading: isLoadingSalary } = useQuery({
|
|
|
queryKey: ['statistics', 'salary-distribution'],
|
|
queryKey: ['statistics', 'salary-distribution'],
|
|
|
queryFn: async () => {
|
|
queryFn: async () => {
|
|
|
- const response = await enterpriseStatisticsClient['salary-distribution'].$get()
|
|
|
|
|
|
|
+ const response = await enterpriseStatisticsClient['salary-distribution'].$get({ query: {} })
|
|
|
return await response.json()
|
|
return await response.json()
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
@@ -131,33 +151,61 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
<Text className="font-semibold text-gray-700 mb-3">残疾类型分布</Text>
|
|
<Text className="font-semibold text-gray-700 mb-3">残疾类型分布</Text>
|
|
|
{isLoadingDisability ? (
|
|
{isLoadingDisability ? (
|
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
|
- ) : disabilityData?.stats && disabilityData.stats.length > 0 ? (
|
|
|
|
|
- <>
|
|
|
|
|
- <View className="chart-container mb-2 h-40 relative">
|
|
|
|
|
- {disabilityData.stats.map((item: DisabilityTypeDistributionResponse['stats'][0], index: number) => {
|
|
|
|
|
- const maxValue = Math.max(...disabilityData.stats.map((s: DisabilityTypeDistributionResponse['stats'][0]) => s.value || 0))
|
|
|
|
|
- const height = maxValue > 0 ? ((item.value || 0) / maxValue) * 160 : 0
|
|
|
|
|
- const left = 20 + index * 50
|
|
|
|
|
- return (
|
|
|
|
|
- <View
|
|
|
|
|
- key={item.key}
|
|
|
|
|
- className="chart-bar absolute bottom-0 bg-blue-500 rounded-t-lg"
|
|
|
|
|
- style={{ left: `${left}px`, height: `${height}px`, width: '30px' }}
|
|
|
|
|
- />
|
|
|
|
|
- )
|
|
|
|
|
- })}
|
|
|
|
|
- </View>
|
|
|
|
|
- <View className="flex justify-between text-xs text-gray-500">
|
|
|
|
|
- {disabilityData.stats.map((item: DisabilityTypeDistributionResponse['stats'][0]) => (
|
|
|
|
|
- <View key={item.key} className="flex flex-col items-center">
|
|
|
|
|
- <Text>{item.key}</Text>
|
|
|
|
|
- <Text className="text-xs text-gray-400">{item.percentage}%</Text>
|
|
|
|
|
- </View>
|
|
|
|
|
- ))}
|
|
|
|
|
- </View>
|
|
|
|
|
- </>
|
|
|
|
|
) : (
|
|
) : (
|
|
|
- <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
|
|
+ (() => {
|
|
|
|
|
+ const stats = getStats(disabilityData)
|
|
|
|
|
+ if (stats.length > 0) {
|
|
|
|
|
+ return (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <View className="chart-container mb-2 h-40 relative">
|
|
|
|
|
+ {stats.map((item: any, index: number) => {
|
|
|
|
|
+ const maxValue = Math.max(...stats.map((s: any) => s.value || 0))
|
|
|
|
|
+ const height = maxValue > 0 ? ((item.value || 0) / maxValue) * 160 : 0
|
|
|
|
|
+ const left = 20 + index * 50
|
|
|
|
|
+ return (
|
|
|
|
|
+ <View
|
|
|
|
|
+ key={item.key}
|
|
|
|
|
+ className="chart-bar absolute bottom-0 bg-blue-500 rounded-t-lg"
|
|
|
|
|
+ style={{ left: `${left}px`, height: `${height}px`, width: '30px' }}
|
|
|
|
|
+ onClick={() => handleDisabilityTypeClick(item)}
|
|
|
|
|
+ />
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View className="flex justify-between text-xs text-gray-500">
|
|
|
|
|
+ {stats.map((item: any) => (
|
|
|
|
|
+ <View key={item.key} className="flex flex-col items-center">
|
|
|
|
|
+ <Text>{item.key}</Text>
|
|
|
|
|
+ <Text className="text-xs text-gray-400">{item.percentage}%</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </View>
|
|
|
|
|
+ {/* 数据表格展示 */}
|
|
|
|
|
+ <View className="mt-4 border-t pt-3">
|
|
|
|
|
+ <Text className="font-medium text-gray-700 mb-2">详细数据表格</Text>
|
|
|
|
|
+ <View className="overflow-x-auto">
|
|
|
|
|
+ <View className="min-w-full">
|
|
|
|
|
+ <View className="flex border-b py-2">
|
|
|
|
|
+ <Text className="flex-1 font-medium text-gray-600">残疾类型</Text>
|
|
|
|
|
+ <Text className="flex-1 font-medium text-gray-600">人数</Text>
|
|
|
|
|
+ <Text className="flex-1 font-medium text-gray-600">百分比</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ {stats.map((item: any) => (
|
|
|
|
|
+ <View key={item.key} className="flex border-b py-2">
|
|
|
|
|
+ <Text className="flex-1 text-gray-800">{item.key}</Text>
|
|
|
|
|
+ <Text className="flex-1 text-gray-600">{item.value}人</Text>
|
|
|
|
|
+ <Text className="flex-1 text-gray-600">{item.percentage}%</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </>
|
|
|
|
|
+ )
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
+ }
|
|
|
|
|
+ })()
|
|
|
)}
|
|
)}
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
@@ -166,29 +214,32 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
<Text className="font-semibold text-gray-700 mb-3">性别分布</Text>
|
|
<Text className="font-semibold text-gray-700 mb-3">性别分布</Text>
|
|
|
{isLoadingGender ? (
|
|
{isLoadingGender ? (
|
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
|
- ) : genderData?.stats && genderData.stats.length > 0 ? (
|
|
|
|
|
- <View className="bar-chart flex items-end justify-center gap-10 h-32">
|
|
|
|
|
- {genderData.stats.map((item: GenderDistributionResponse['stats'][0]) => {
|
|
|
|
|
- const maxValue = Math.max(...genderData.stats.map((s: GenderDistributionResponse['stats'][0]) => s.value || 0))
|
|
|
|
|
- const height = maxValue > 0 ? ((item.value || 0) / maxValue) * 100 : 0
|
|
|
|
|
- const color = item.key === '男' ? '#3b82f6' : '#ec4899'
|
|
|
|
|
- return (
|
|
|
|
|
- <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>
|
|
|
|
|
- <View
|
|
|
|
|
- className="bar rounded-t-lg"
|
|
|
|
|
- style={{ height: `${height}px`, width: '40px', backgroundColor: color }}
|
|
|
|
|
- />
|
|
|
|
|
- <Text className="bar-label text-xs text-gray-500 mt-2">
|
|
|
|
|
- {item.key} ({item.percentage}%)
|
|
|
|
|
- </Text>
|
|
|
|
|
- </View>
|
|
|
|
|
- )
|
|
|
|
|
- })}
|
|
|
|
|
- </View>
|
|
|
|
|
- ) : (
|
|
|
|
|
- <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
- )}
|
|
|
|
|
|
|
+ ) : (() => {
|
|
|
|
|
+ const genderStats = getStats(genderData)
|
|
|
|
|
+ return genderStats.length > 0 ? (
|
|
|
|
|
+ <View className="bar-chart flex items-end justify-center gap-10 h-32">
|
|
|
|
|
+ {genderStats.map((item: any, index: number) => {
|
|
|
|
|
+ const maxValue = Math.max(...genderStats.map((s: any) => s.value || 0))
|
|
|
|
|
+ const height = maxValue > 0 ? ((item.value || 0) / maxValue) * 100 : 0
|
|
|
|
|
+ const color = item.key === '男' ? '#3b82f6' : '#ec4899'
|
|
|
|
|
+ return (
|
|
|
|
|
+ <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>
|
|
|
|
|
+ <View
|
|
|
|
|
+ className="bar rounded-t-lg"
|
|
|
|
|
+ style={{ height: `${height}px`, width: '40px', backgroundColor: color }}
|
|
|
|
|
+ />
|
|
|
|
|
+ <Text className="bar-label text-xs text-gray-500 mt-2">
|
|
|
|
|
+ {item.key} ({item.percentage}%)
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
|
|
+ </View>
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
+ )
|
|
|
|
|
+ })()}
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
{/* 年龄分布 */}
|
|
{/* 年龄分布 */}
|
|
@@ -196,35 +247,48 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
<Text className="font-semibold text-gray-700 mb-3">年龄分布</Text>
|
|
<Text className="font-semibold text-gray-700 mb-3">年龄分布</Text>
|
|
|
{isLoadingAge ? (
|
|
{isLoadingAge ? (
|
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
|
- ) : ageData?.stats && ageData.stats.length > 0 ? (
|
|
|
|
|
- <>
|
|
|
|
|
- <View className="pie-chart-container flex justify-center mb-4">
|
|
|
|
|
- {/* 简单的饼图表示 - 实际项目可替换为SVG饼图 */}
|
|
|
|
|
- <View className="w-32 h-32 rounded-full border-4 border-gray-300 flex items-center justify-center">
|
|
|
|
|
- <Text className="text-gray-500">饼图</Text>
|
|
|
|
|
- </View>
|
|
|
|
|
- </View>
|
|
|
|
|
- <View className="pie-legend flex flex-wrap justify-center gap-3">
|
|
|
|
|
- {ageData.stats.map((item: AgeDistributionResponse['stats'][0], index: number) => {
|
|
|
|
|
- const colors = ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#8b5cf6']
|
|
|
|
|
- const color = colors[index % colors.length]
|
|
|
|
|
- return (
|
|
|
|
|
- <View key={item.key} className="legend-item flex items-center">
|
|
|
|
|
- <View
|
|
|
|
|
- className="legend-color w-3 h-3 rounded-sm mr-2"
|
|
|
|
|
- style={{ backgroundColor: color }}
|
|
|
|
|
- />
|
|
|
|
|
- <Text className="text-xs text-gray-700">
|
|
|
|
|
- {item.key} ({item.percentage}%)
|
|
|
|
|
- </Text>
|
|
|
|
|
|
|
+ ) : (() => {
|
|
|
|
|
+ const ageStats = getStats(ageData)
|
|
|
|
|
+ return ageStats.length > 0 ? (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <View className="pie-chart-container flex justify-center mb-4">
|
|
|
|
|
+ {/* 简单的饼图表示 - 使用conic-gradient模拟饼图 */}
|
|
|
|
|
+ <View
|
|
|
|
|
+ className="w-32 h-32 rounded-full"
|
|
|
|
|
+ style={{
|
|
|
|
|
+ background: `conic-gradient(
|
|
|
|
|
+ #3b82f6 0% ${ageStats[0]?.percentage || 0}%,
|
|
|
|
|
+ #10b981 ${ageStats[0]?.percentage || 0}% ${(ageStats[0]?.percentage || 0) + (ageStats[1]?.percentage || 0)}%,
|
|
|
|
|
+ #f59e0b ${(ageStats[0]?.percentage || 0) + (ageStats[1]?.percentage || 0)}% ${(ageStats[0]?.percentage || 0) + (ageStats[1]?.percentage || 0) + (ageStats[2]?.percentage || 0)}%,
|
|
|
|
|
+ #8b5cf6 ${(ageStats[0]?.percentage || 0) + (ageStats[1]?.percentage || 0) + (ageStats[2]?.percentage || 0)}% 100%
|
|
|
|
|
+ )`
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <View className="w-20 h-20 bg-white rounded-full absolute top-6 left-6"></View>
|
|
|
</View>
|
|
</View>
|
|
|
- )
|
|
|
|
|
- })}
|
|
|
|
|
- </View>
|
|
|
|
|
- </>
|
|
|
|
|
- ) : (
|
|
|
|
|
- <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
- )}
|
|
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View className="pie-legend flex flex-wrap justify-center gap-3">
|
|
|
|
|
+ {ageStats.map((item: any, index: number) => {
|
|
|
|
|
+ const colors = ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#8b5cf6']
|
|
|
|
|
+ const color = colors[index % colors.length]
|
|
|
|
|
+ return (
|
|
|
|
|
+ <View key={item.key} className="legend-item flex items-center">
|
|
|
|
|
+ <View
|
|
|
|
|
+ className="legend-color w-3 h-3 rounded-sm mr-2"
|
|
|
|
|
+ style={{ backgroundColor: color }}
|
|
|
|
|
+ />
|
|
|
|
|
+ <Text className="text-xs text-gray-700">
|
|
|
|
|
+ {item.key} ({item.percentage}%)
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </>
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
+ )
|
|
|
|
|
+ })()}
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
{/* 户籍省份分布 */}
|
|
{/* 户籍省份分布 */}
|
|
@@ -232,32 +296,37 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
<Text className="font-semibold text-gray-700 mb-3">户籍省份分布</Text>
|
|
<Text className="font-semibold text-gray-700 mb-3">户籍省份分布</Text>
|
|
|
{isLoadingHousehold ? (
|
|
{isLoadingHousehold ? (
|
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
|
- ) : householdData?.stats && householdData.stats.length > 0 ? (
|
|
|
|
|
- <View className="space-y-3">
|
|
|
|
|
- {householdData.stats.slice(0, 6).map((item: HouseholdDistributionResponse['stats'][0], index: number) => {
|
|
|
|
|
- const maxValue = Math.max(...householdData.stats.map((s: HouseholdDistributionResponse['stats'][0]) => s.value || 0))
|
|
|
|
|
- const width = maxValue > 0 ? ((item.value || 0) / maxValue) * 100 : 0
|
|
|
|
|
- const colors = ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#ec4899']
|
|
|
|
|
- const color = colors[index % colors.length]
|
|
|
|
|
|
|
+ ) : (() => {
|
|
|
|
|
+ const stats = getStats(householdData)
|
|
|
|
|
+ if (stats.length > 0) {
|
|
|
return (
|
|
return (
|
|
|
- <View key={item.key}>
|
|
|
|
|
- <View className="flex justify-between text-sm mb-1">
|
|
|
|
|
- <Text className="text-gray-700">{item.key}</Text>
|
|
|
|
|
- <Text className="text-gray-500">{item.value}人</Text>
|
|
|
|
|
- </View>
|
|
|
|
|
- <View className="progress-bar h-2 bg-gray-200 rounded-full overflow-hidden">
|
|
|
|
|
- <View
|
|
|
|
|
- className="progress-fill h-full rounded-full"
|
|
|
|
|
- style={{ width: `${width}%`, backgroundColor: color }}
|
|
|
|
|
- />
|
|
|
|
|
- </View>
|
|
|
|
|
|
|
+ <View className="space-y-3">
|
|
|
|
|
+ {stats.slice(0, 6).map((item: HouseholdDistributionResponse['stats'][0], index: number) => {
|
|
|
|
|
+ const maxValue = Math.max(...stats.map((s: HouseholdDistributionResponse['stats'][0]) => s.value || 0))
|
|
|
|
|
+ const width = maxValue > 0 ? ((item.value || 0) / maxValue) * 100 : 0
|
|
|
|
|
+ const colors = ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#ec4899']
|
|
|
|
|
+ const color = colors[index % colors.length]
|
|
|
|
|
+ return (
|
|
|
|
|
+ <View key={item.key}>
|
|
|
|
|
+ <View className="flex justify-between text-sm mb-1">
|
|
|
|
|
+ <Text className="text-gray-700">{item.key}</Text>
|
|
|
|
|
+ <Text className="text-gray-500">{item.value}人</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View className="progress-bar h-2 bg-gray-200 rounded-full overflow-hidden">
|
|
|
|
|
+ <View
|
|
|
|
|
+ className="progress-fill h-full rounded-full"
|
|
|
|
|
+ style={{ width: `${width}%`, backgroundColor: color }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
</View>
|
|
</View>
|
|
|
)
|
|
)
|
|
|
- })}
|
|
|
|
|
- </View>
|
|
|
|
|
- ) : (
|
|
|
|
|
- <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
- )}
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
+ }
|
|
|
|
|
+ })()}
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
{/* 在职状态统计 */}
|
|
{/* 在职状态统计 */}
|
|
@@ -265,31 +334,36 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
<Text className="font-semibold text-gray-700 mb-3">在职状态统计</Text>
|
|
<Text className="font-semibold text-gray-700 mb-3">在职状态统计</Text>
|
|
|
{isLoadingJobStatus ? (
|
|
{isLoadingJobStatus ? (
|
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
|
- ) : jobStatusData?.stats && jobStatusData.stats.length > 0 ? (
|
|
|
|
|
- <View className="flex items-center justify-center">
|
|
|
|
|
- {/* 环形图占位 */}
|
|
|
|
|
- <View className="w-32 h-32 rounded-full border-8 border-blue-500 border-t-transparent transform -rotate-45" />
|
|
|
|
|
- <View className="ml-6">
|
|
|
|
|
- {jobStatusData.stats.map((item: JobStatusDistributionResponse['stats'][0], index: number) => {
|
|
|
|
|
- const colors = ['#3b82f6', '#f59e0b', '#ef4444', '#10b981']
|
|
|
|
|
- const color = colors[index % colors.length]
|
|
|
|
|
- return (
|
|
|
|
|
- <View key={item.key} className="flex items-center mb-2">
|
|
|
|
|
- <View
|
|
|
|
|
- className="w-3 h-3 rounded-full mr-2"
|
|
|
|
|
- style={{ backgroundColor: color }}
|
|
|
|
|
- />
|
|
|
|
|
- <Text className="text-sm text-gray-700">
|
|
|
|
|
- {item.key}: {item.value}人 ({item.percentage}%)
|
|
|
|
|
- </Text>
|
|
|
|
|
|
|
+ ) : (() => {
|
|
|
|
|
+ const stats = getStats(jobStatusData)
|
|
|
|
|
+ if (stats.length > 0) {
|
|
|
|
|
+ return (
|
|
|
|
|
+ <View className="flex items-center justify-center">
|
|
|
|
|
+ {/* 环形图占位 */}
|
|
|
|
|
+ <View className="w-32 h-32 rounded-full border-8 border-blue-500 border-t-transparent transform -rotate-45" />
|
|
|
|
|
+ <View className="ml-6">
|
|
|
|
|
+ {stats.map((item: JobStatusDistributionResponse['stats'][0], index: number) => {
|
|
|
|
|
+ const colors = ['#3b82f6', '#f59e0b', '#ef4444', '#10b981']
|
|
|
|
|
+ const color = colors[index % colors.length]
|
|
|
|
|
+ return (
|
|
|
|
|
+ <View key={item.key} className="flex items-center mb-2">
|
|
|
|
|
+ <View
|
|
|
|
|
+ className="w-3 h-3 rounded-full mr-2"
|
|
|
|
|
+ style={{ backgroundColor: color }}
|
|
|
|
|
+ />
|
|
|
|
|
+ <Text className="text-sm text-gray-700">
|
|
|
|
|
+ {item.key}: {item.value}人 ({item.percentage}%)
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
</View>
|
|
</View>
|
|
|
- )
|
|
|
|
|
- })}
|
|
|
|
|
- </View>
|
|
|
|
|
- </View>
|
|
|
|
|
- ) : (
|
|
|
|
|
- <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
- )}
|
|
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
+ }
|
|
|
|
|
+ })()}
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
{/* 薪资分布 */}
|
|
{/* 薪资分布 */}
|
|
@@ -297,32 +371,37 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
<Text className="font-semibold text-gray-700 mb-3">薪资分布</Text>
|
|
<Text className="font-semibold text-gray-700 mb-3">薪资分布</Text>
|
|
|
{isLoadingSalary ? (
|
|
{isLoadingSalary ? (
|
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
<Text className="text-gray-500 text-center py-4">加载中...</Text>
|
|
|
- ) : salaryData?.stats && salaryData.stats.length > 0 ? (
|
|
|
|
|
- <View className="space-y-3">
|
|
|
|
|
- {salaryData.stats.map((item: SalaryDistributionResponse['stats'][0], index: number) => {
|
|
|
|
|
- const maxValue = Math.max(...salaryData.stats.map((s: SalaryDistributionResponse['stats'][0]) => s.value || 0))
|
|
|
|
|
- const width = maxValue > 0 ? ((item.value || 0) / maxValue) * 100 : 0
|
|
|
|
|
- const colors = ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444']
|
|
|
|
|
- const color = colors[index % colors.length]
|
|
|
|
|
|
|
+ ) : (() => {
|
|
|
|
|
+ const stats = getStats(salaryData)
|
|
|
|
|
+ if (stats.length > 0) {
|
|
|
return (
|
|
return (
|
|
|
- <View key={item.key}>
|
|
|
|
|
- <View className="flex justify-between text-sm mb-1">
|
|
|
|
|
- <Text className="text-gray-700">{item.key}</Text>
|
|
|
|
|
- <Text className="text-gray-500">{item.value}人</Text>
|
|
|
|
|
- </View>
|
|
|
|
|
- <View className="progress-bar h-2 bg-gray-200 rounded-full overflow-hidden">
|
|
|
|
|
- <View
|
|
|
|
|
- className="progress-fill h-full rounded-full"
|
|
|
|
|
- style={{ width: `${width}%`, backgroundColor: color }}
|
|
|
|
|
- />
|
|
|
|
|
- </View>
|
|
|
|
|
|
|
+ <View className="space-y-3">
|
|
|
|
|
+ {stats.map((item: SalaryDistributionResponse['stats'][0], index: number) => {
|
|
|
|
|
+ const maxValue = Math.max(...stats.map((s: SalaryDistributionResponse['stats'][0]) => s.value || 0))
|
|
|
|
|
+ const width = maxValue > 0 ? ((item.value || 0) / maxValue) * 100 : 0
|
|
|
|
|
+ const colors = ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444']
|
|
|
|
|
+ const color = colors[index % colors.length]
|
|
|
|
|
+ return (
|
|
|
|
|
+ <View key={item.key}>
|
|
|
|
|
+ <View className="flex justify-between text-sm mb-1">
|
|
|
|
|
+ <Text className="text-gray-700">{item.key}</Text>
|
|
|
|
|
+ <Text className="text-gray-500">{item.value}人</Text>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View className="progress-bar h-2 bg-gray-200 rounded-full overflow-hidden">
|
|
|
|
|
+ <View
|
|
|
|
|
+ className="progress-fill h-full rounded-full"
|
|
|
|
|
+ style={{ width: `${width}%`, backgroundColor: color }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )
|
|
|
|
|
+ })}
|
|
|
</View>
|
|
</View>
|
|
|
)
|
|
)
|
|
|
- })}
|
|
|
|
|
- </View>
|
|
|
|
|
- ) : (
|
|
|
|
|
- <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
- )}
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return <Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
|
|
+ }
|
|
|
|
|
+ })()}
|
|
|
</View>
|
|
</View>
|
|
|
</ScrollView>
|
|
</ScrollView>
|
|
|
</YongrenTabBarLayout>
|
|
</YongrenTabBarLayout>
|