feat: 获取总数分类
This commit is contained in:
parent
86bdc7089b
commit
0307ed0a00
@ -43,6 +43,7 @@ export default function UserMessage() {
|
|||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getUserInfo();
|
getUserInfo();
|
||||||
|
setSteps("userName")
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
333
components/file-upload/getTotal.tsx
Normal file
333
components/file-upload/getTotal.tsx
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
import * as MediaLibrary from 'expo-media-library';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { ActivityIndicator, Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
|
||||||
|
interface MediaStats {
|
||||||
|
total: number;
|
||||||
|
photos: number;
|
||||||
|
videos: number;
|
||||||
|
audios: number;
|
||||||
|
others: number;
|
||||||
|
byMonth: Record<string, number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimeRange = 'day' | 'week' | 'month' | 'all';
|
||||||
|
|
||||||
|
const MediaStatsScreen = () => {
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [stats, setStats] = useState<MediaStats | null>(null);
|
||||||
|
const [timeRange, setTimeRange] = useState<TimeRange>('week'); // 默认显示一周
|
||||||
|
|
||||||
|
const getDateRange = (range: TimeRange) => {
|
||||||
|
const now = new Date();
|
||||||
|
const start = new Date(now);
|
||||||
|
|
||||||
|
switch (range) {
|
||||||
|
case 'day':
|
||||||
|
start.setDate(now.getDate() - 1);
|
||||||
|
break;
|
||||||
|
case 'week':
|
||||||
|
start.setDate(now.getDate() - 7);
|
||||||
|
break;
|
||||||
|
case 'month':
|
||||||
|
start.setMonth(now.getMonth() - 1);
|
||||||
|
break;
|
||||||
|
case 'all':
|
||||||
|
default:
|
||||||
|
return null; // 返回 null 表示不限制时间范围
|
||||||
|
}
|
||||||
|
|
||||||
|
return { start, end: now };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMediaStatistics = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
// 1. 请求媒体库权限
|
||||||
|
const { status } = await MediaLibrary.requestPermissionsAsync();
|
||||||
|
if (status !== 'granted') {
|
||||||
|
Alert.alert('权限被拒绝', '需要访问媒体库权限来获取统计信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 设置时间范围
|
||||||
|
const dateRange = getDateRange(timeRange);
|
||||||
|
const createdAfter = dateRange ? Math.floor(dateRange.start.getTime() / 1000) : 0;
|
||||||
|
const endTime = dateRange?.end ? Math.floor(dateRange.end.getTime() / 1000) : undefined;
|
||||||
|
|
||||||
|
// 3. 分页获取媒体资源,每次10条
|
||||||
|
let hasNextPage = true;
|
||||||
|
let after = undefined;
|
||||||
|
let allAssets: MediaLibrary.Asset[] = [];
|
||||||
|
const pageSize = 10; // 每次获取10条
|
||||||
|
|
||||||
|
while (hasNextPage) {
|
||||||
|
const media = await MediaLibrary.getAssetsAsync({
|
||||||
|
first: pageSize,
|
||||||
|
after,
|
||||||
|
mediaType: ['photo', 'video', 'audio', 'unknown'],
|
||||||
|
sortBy: 'creationTime', // 按创建时间降序,最新的在前面
|
||||||
|
createdAfter: Date.now() - 24 * 30 * 12 * 60 * 60 * 1000, // 时间戳(毫秒)
|
||||||
|
createdBefore: Date.now(), // 时间戳(毫秒)
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果没有数据,直接退出
|
||||||
|
if (media.assets.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查每条记录是否在时间范围内
|
||||||
|
for (const asset of media.assets) {
|
||||||
|
const assetTime = asset.creationTime ? new Date(asset.creationTime).getTime() / 1000 : 0;
|
||||||
|
|
||||||
|
// 如果设置了结束时间,并且当前记录的时间早于开始时间,则停止
|
||||||
|
if (endTime && assetTime > endTime) {
|
||||||
|
continue; // 跳过这条记录
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果设置了开始时间,并且当前记录的时间早于开始时间,则停止
|
||||||
|
if (createdAfter && assetTime < createdAfter) {
|
||||||
|
hasNextPage = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
allAssets.push(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新游标和是否还有下一页
|
||||||
|
hasNextPage = media.hasNextPage && media.assets.length === pageSize;
|
||||||
|
after = media.endCursor;
|
||||||
|
|
||||||
|
// 如果没有更多数据或者已经获取了足够的数据
|
||||||
|
if (!hasNextPage || allAssets.length >= 1000) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`总共获取到 ${allAssets.length} 个媒体文件`);
|
||||||
|
|
||||||
|
// 4. 统计不同类型媒体的数量
|
||||||
|
const stats: MediaStats = {
|
||||||
|
total: allAssets.length,
|
||||||
|
photos: 0,
|
||||||
|
videos: 0,
|
||||||
|
audios: 0,
|
||||||
|
others: 0,
|
||||||
|
byMonth: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
allAssets.forEach(asset => {
|
||||||
|
// 统计类型
|
||||||
|
if (asset.mediaType === 'photo') stats.photos++;
|
||||||
|
else if (asset.mediaType === 'video') stats.videos++;
|
||||||
|
else if (asset.mediaType === 'audio') stats.audios++;
|
||||||
|
else stats.others++;
|
||||||
|
|
||||||
|
// 按月份统计
|
||||||
|
if (asset.creationTime) {
|
||||||
|
const date = new Date(asset.creationTime);
|
||||||
|
const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
|
||||||
|
stats.byMonth[monthKey] = (stats.byMonth[monthKey] || 0) + 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setStats(stats);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取媒体库统计信息失败:', error);
|
||||||
|
Alert.alert('错误', '获取媒体库统计信息失败');
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 时间范围选择器
|
||||||
|
const TimeRangeSelector = () => (
|
||||||
|
<View style={styles.timeRangeContainer}>
|
||||||
|
{(['day', 'week', 'month', 'all'] as TimeRange[]).map((range) => (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={range}
|
||||||
|
style={[
|
||||||
|
styles.timeRangeButton,
|
||||||
|
timeRange === range && styles.timeRangeButtonActive
|
||||||
|
]}
|
||||||
|
onPress={() => {
|
||||||
|
setTimeRange(range);
|
||||||
|
}}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
<Text style={[
|
||||||
|
styles.timeRangeButtonText,
|
||||||
|
timeRange === range && styles.timeRangeButtonTextActive
|
||||||
|
]}>
|
||||||
|
{{
|
||||||
|
day: '今天',
|
||||||
|
week: '本周',
|
||||||
|
month: '本月',
|
||||||
|
all: '全部'
|
||||||
|
}[range]}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.header}>
|
||||||
|
<Text style={styles.title}>媒体库统计</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TimeRangeSelector />
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.button, isLoading && styles.buttonDisabled]}
|
||||||
|
onPress={getMediaStatistics}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<ActivityIndicator color="#fff" />
|
||||||
|
) : (
|
||||||
|
<Text style={styles.buttonText}>获取媒体库统计</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
{stats && (
|
||||||
|
<View style={styles.statsContainer}>
|
||||||
|
<View style={styles.statsRow}>
|
||||||
|
<StatItem label="总文件数" value={stats.total.toString()} />
|
||||||
|
<StatItem label="照片" value={stats.photos.toString()} />
|
||||||
|
</View>
|
||||||
|
<View style={styles.statsRow}>
|
||||||
|
<StatItem label="视频" value={stats.videos.toString()} />
|
||||||
|
<StatItem label="音频" value={stats.audios.toString()} />
|
||||||
|
<StatItem label="其他" value={stats.others.toString()} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.monthlyContainer}>
|
||||||
|
<Text style={styles.sectionTitle}>按月统计</Text>
|
||||||
|
{Object.entries(stats.byMonth)
|
||||||
|
.sort(([a], [b]) => b.localeCompare(a))
|
||||||
|
.map(([month, count]) => (
|
||||||
|
<View key={month} style={styles.monthlyItem}>
|
||||||
|
<Text style={styles.monthText}>{month}</Text>
|
||||||
|
<Text style={styles.countText}>{count} 个文件</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const StatItem = ({ label, value }: { label: string; value: string }) => (
|
||||||
|
<View style={styles.statItem}>
|
||||||
|
<Text style={styles.statValue}>{value}</Text>
|
||||||
|
<Text style={styles.statLabel}>{label}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
marginBottom: 16,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#333',
|
||||||
|
},
|
||||||
|
timeRangeContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: 16,
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
},
|
||||||
|
timeRangeButton: {
|
||||||
|
paddingVertical: 8,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
borderRadius: 16,
|
||||||
|
backgroundColor: '#f0f0f0',
|
||||||
|
},
|
||||||
|
timeRangeButtonActive: {
|
||||||
|
backgroundColor: '#007AFF',
|
||||||
|
},
|
||||||
|
timeRangeButtonText: {
|
||||||
|
color: '#666',
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
timeRangeButtonTextActive: {
|
||||||
|
color: '#fff',
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
backgroundColor: '#007AFF',
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderRadius: 8,
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
buttonDisabled: {
|
||||||
|
opacity: 0.6,
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
},
|
||||||
|
statsContainer: {
|
||||||
|
backgroundColor: '#f8f8f8',
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
statsRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
statItem: {
|
||||||
|
alignItems: 'center',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
statValue: {
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#007AFF',
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
statLabel: {
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#666',
|
||||||
|
},
|
||||||
|
monthlyContainer: {
|
||||||
|
marginTop: 16,
|
||||||
|
},
|
||||||
|
sectionTitle: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '600',
|
||||||
|
color: '#333',
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
monthlyItem: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
paddingVertical: 10,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#eee',
|
||||||
|
},
|
||||||
|
monthText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#333',
|
||||||
|
},
|
||||||
|
countText: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#666',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default MediaStatsScreen;
|
||||||
@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { ActivityIndicator, Image, TouchableOpacity, View } from 'react-native';
|
import { ActivityIndicator, Image, TouchableOpacity, View } from 'react-native';
|
||||||
import AutoUploadScreen from '../file-upload/autoUploadScreen';
|
import AutoUploadScreen from '../file-upload/autoUploadScreen';
|
||||||
import FilesUploader from '../file-upload/files-uploader';
|
import FilesUploader from '../file-upload/files-uploader';
|
||||||
|
import MediaStatsScreen from '../file-upload/getTotal';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setSteps?: (steps: Steps) => void;
|
setSteps?: (steps: Steps) => void;
|
||||||
@ -64,6 +65,7 @@ export default function Look(props: Props) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<AutoUploadScreen />
|
<AutoUploadScreen />
|
||||||
|
<MediaStatsScreen />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className="w-full">
|
<View className="w-full">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user