|
|
@@ -3,6 +3,7 @@ import { View, Text, ScrollView, Picker } from '@tarojs/components'
|
|
|
import { useQuery } from '@tanstack/react-query'
|
|
|
import { YongrenTabBarLayout } from '@d8d/yongren-shared-ui/components/YongrenTabBarLayout'
|
|
|
import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
|
|
|
+import { ColumnChart, PieChart, BarChart } from '@d8d/mini-charts'
|
|
|
import { enterpriseStatisticsClient } from '../../api/enterpriseStatisticsClient'
|
|
|
import type {
|
|
|
DisabilityTypeDistributionResponse,
|
|
|
@@ -13,6 +14,26 @@ import type {
|
|
|
SalaryDistributionResponse
|
|
|
} from '../../api/types'
|
|
|
|
|
|
+/**
|
|
|
+ * 数据转换工具:将API统计数据转换为柱状图格式
|
|
|
+ */
|
|
|
+const convertToColumnData = (stats: any[]) => ({
|
|
|
+ categories: stats.map(item => item.key),
|
|
|
+ series: [{
|
|
|
+ name: '人数',
|
|
|
+ data: stats.map(item => item.value || 0)
|
|
|
+ }]
|
|
|
+})
|
|
|
+
|
|
|
+/**
|
|
|
+ * 数据转换工具:将API统计数据转换为饼图格式
|
|
|
+ */
|
|
|
+const convertToPieData = (stats: any[]) =>
|
|
|
+ stats.map(item => ({
|
|
|
+ name: item.key,
|
|
|
+ data: item.value || 0
|
|
|
+ }))
|
|
|
+
|
|
|
export interface StatisticsProps {
|
|
|
// 组件属性定义(目前为空)
|
|
|
}
|
|
|
@@ -235,31 +256,28 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
(() => {
|
|
|
const stats = getStats(disabilityData)
|
|
|
if (stats.length > 0) {
|
|
|
+ const chartData = convertToColumnData(stats)
|
|
|
return (
|
|
|
- <>
|
|
|
- <View className="chart-container mb-2 mt-3 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', minHeight: '8px' }}
|
|
|
- />
|
|
|
- )
|
|
|
- })}
|
|
|
- </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-3">
|
|
|
+ <ColumnChart
|
|
|
+ canvasId="disability-type-chart"
|
|
|
+ width={650}
|
|
|
+ height={200}
|
|
|
+ categories={chartData.categories}
|
|
|
+ series={chartData.series}
|
|
|
+ config={{
|
|
|
+ color: ['#3b82f6'],
|
|
|
+ fontSize: 10,
|
|
|
+ dataLabel: true,
|
|
|
+ xAxis: { disableGrid: true },
|
|
|
+ yAxis: {}
|
|
|
+ }}
|
|
|
+ tooltipFormatter={(item: any, category: string) => {
|
|
|
+ const statItem = stats.find(s => s.key === category)
|
|
|
+ return `${category} ${item.data}人 (${statItem?.percentage || 0}%)`
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
)
|
|
|
} else {
|
|
|
return <Text className="text-gray-500 text-center py-4 mt-3">暂无数据</Text>
|
|
|
@@ -276,24 +294,28 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
) : (() => {
|
|
|
const genderStats = getStats(genderData)
|
|
|
return genderStats.length > 0 ? (
|
|
|
- <View className="bar-chart flex items-end justify-center gap-10 h-32 mt-3">
|
|
|
- {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, minHeight: '4px' }}
|
|
|
- />
|
|
|
- <Text className="bar-label text-xs text-gray-500 mt-2">
|
|
|
- {item.key} ({item.percentage}%)
|
|
|
- </Text>
|
|
|
- </View>
|
|
|
- )
|
|
|
- })}
|
|
|
+ <View className="mt-3">
|
|
|
+ <ColumnChart
|
|
|
+ canvasId="gender-chart"
|
|
|
+ width={650}
|
|
|
+ height={200}
|
|
|
+ categories={genderStats.map(s => s.key)}
|
|
|
+ series={[{
|
|
|
+ name: '人数',
|
|
|
+ data: genderStats.map(s => s.value || 0)
|
|
|
+ }]}
|
|
|
+ config={{
|
|
|
+ color: ['#3b82f6', '#ec4899'],
|
|
|
+ fontSize: 11,
|
|
|
+ dataLabel: true,
|
|
|
+ xAxis: { disableGrid: true },
|
|
|
+ yAxis: {}
|
|
|
+ }}
|
|
|
+ tooltipFormatter={(item: any, category: string) => {
|
|
|
+ const statItem = genderStats.find(s => s.key === category)
|
|
|
+ return `${category} ${item.data}人 (${statItem?.percentage || 0}%)`
|
|
|
+ }}
|
|
|
+ />
|
|
|
</View>
|
|
|
) : (
|
|
|
<Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
@@ -309,41 +331,25 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
) : (() => {
|
|
|
const ageStats = getStats(ageData)
|
|
|
return ageStats.length > 0 ? (
|
|
|
- <>
|
|
|
- <View className="pie-chart-container flex justify-center mb-4 mt-3">
|
|
|
- {/* 简单的饼图表示 - 使用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 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>
|
|
|
- </>
|
|
|
+ <View className="mt-3">
|
|
|
+ <PieChart
|
|
|
+ canvasId="age-chart"
|
|
|
+ width={650}
|
|
|
+ height={300}
|
|
|
+ pieType="pie"
|
|
|
+ series={convertToPieData(ageStats)}
|
|
|
+ config={{
|
|
|
+ color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
|
|
|
+ fontSize: 10,
|
|
|
+ dataLabel: true,
|
|
|
+ legend: { show: true }
|
|
|
+ }}
|
|
|
+ tooltipFormatter={(item: any) => {
|
|
|
+ const statItem = ageStats.find(s => s.key === item.name)
|
|
|
+ return `${item.name} ${item.data}人 (${statItem?.percentage || 0}%)`
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
) : (
|
|
|
<Text className="text-gray-500 text-center py-4">暂无数据</Text>
|
|
|
)
|
|
|
@@ -358,28 +364,27 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
) : (() => {
|
|
|
const stats = getStats(householdData)
|
|
|
if (stats.length > 0) {
|
|
|
+ const chartData = convertToColumnData(stats.slice(0, 6))
|
|
|
return (
|
|
|
- <View className="space-y-3 mt-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} className="flex flex-col">
|
|
|
- <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 className="mt-3">
|
|
|
+ <BarChart
|
|
|
+ canvasId="household-chart"
|
|
|
+ width={650}
|
|
|
+ height={280}
|
|
|
+ categories={chartData.categories}
|
|
|
+ series={chartData.series}
|
|
|
+ config={{
|
|
|
+ color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#ec4899'],
|
|
|
+ fontSize: 10,
|
|
|
+ dataLabel: true,
|
|
|
+ xAxis: { disableGrid: true },
|
|
|
+ yAxis: {}
|
|
|
+ }}
|
|
|
+ tooltipFormatter={(item: any, category: string) => {
|
|
|
+ const statItem = stats.find(s => s.key === category)
|
|
|
+ return `${category} ${item.data}人 (${statItem?.percentage || 0}%)`
|
|
|
+ }}
|
|
|
+ />
|
|
|
</View>
|
|
|
)
|
|
|
} else {
|
|
|
@@ -397,26 +402,24 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
const stats = getStats(jobStatusData)
|
|
|
if (stats.length > 0) {
|
|
|
return (
|
|
|
- <View className="flex items-center justify-center mt-3">
|
|
|
- {/* 环形图占位 */}
|
|
|
- <View className="w-32 h-32 rounded-full border-8 border-blue-500 border-t-transparent transform -rotate-45" />
|
|
|
- <View className="ml-6 flex flex-col">
|
|
|
- {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 className="mt-3">
|
|
|
+ <PieChart
|
|
|
+ canvasId="job-status-chart"
|
|
|
+ width={650}
|
|
|
+ height={300}
|
|
|
+ pieType="ring"
|
|
|
+ series={convertToPieData(stats)}
|
|
|
+ config={{
|
|
|
+ color: ['#3b82f6', '#f59e0b', '#ef4444', '#10b981'],
|
|
|
+ fontSize: 10,
|
|
|
+ dataLabel: true,
|
|
|
+ legend: { show: true }
|
|
|
+ }}
|
|
|
+ tooltipFormatter={(item: any) => {
|
|
|
+ const statItem = stats.find(s => s.key === item.name)
|
|
|
+ return `${item.name} ${item.data}人 (${statItem?.percentage || 0}%)`
|
|
|
+ }}
|
|
|
+ />
|
|
|
</View>
|
|
|
)
|
|
|
} else {
|
|
|
@@ -433,28 +436,27 @@ const Statistics: React.FC<StatisticsProps> = () => {
|
|
|
) : (() => {
|
|
|
const stats = getStats(salaryData)
|
|
|
if (stats.length > 0) {
|
|
|
+ const chartData = convertToColumnData(stats)
|
|
|
return (
|
|
|
- <View className="space-y-3 mt-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} className="flex flex-col">
|
|
|
- <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 className="mt-3">
|
|
|
+ <BarChart
|
|
|
+ canvasId="salary-chart"
|
|
|
+ width={650}
|
|
|
+ height={280}
|
|
|
+ categories={chartData.categories}
|
|
|
+ series={chartData.series}
|
|
|
+ config={{
|
|
|
+ color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
|
|
|
+ fontSize: 10,
|
|
|
+ dataLabel: true,
|
|
|
+ xAxis: { disableGrid: true },
|
|
|
+ yAxis: {}
|
|
|
+ }}
|
|
|
+ tooltipFormatter={(item: any, category: string) => {
|
|
|
+ const statItem = stats.find(s => s.key === category)
|
|
|
+ return `${category} ${item.data}人 (${statItem?.percentage || 0}%)`
|
|
|
+ }}
|
|
|
+ />
|
|
|
</View>
|
|
|
)
|
|
|
} else {
|