import * as MediaLibrary from 'expo-media-library'; import React, { useState } from 'react'; import { ActivityIndicator, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import i18n from '@/i18n'; import { PermissionService } from '@/lib/PermissionService'; interface MediaStats { total: number; photos: number; videos: number; audios: number; others: number; byMonth: Record; } type TimeRange = 'day' | 'week' | 'month' | 'all'; const MediaStatsScreen = () => { const [isLoading, setIsLoading] = useState(false); const [stats, setStats] = useState(null); const [timeRange, setTimeRange] = useState('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') { PermissionService.show({ title: i18n.t('permission:title.permissionDenied'), message: i18n.t('permission:message.getStatsPermissionRequired') }); return; } // 2. 设置时间范围,直接使用Date对象 const dateRange = getDateRange(timeRange); // 3. 分页获取媒体资源 let allAssets: MediaLibrary.Asset[] = []; let hasNextPage = true; let after: MediaLibrary.AssetRef | undefined = undefined; const pageSize = 100; // 增加每次获取的数量以提高效率 while (hasNextPage) { const media = await MediaLibrary.getAssetsAsync({ first: pageSize, after, sortBy: ['creationTime'], mediaType: ['photo', 'video', 'audio'], createdAfter: dateRange?.start, createdBefore: dateRange?.end, }); if (media.assets.length > 0) { allAssets.push(...media.assets); } hasNextPage = media.hasNextPage; after = media.endCursor; // 可选:增加一个最大获取上限,防止无限循环 if (allAssets.length > 2000) { console.warn('已达到2000个媒体文件的上限'); break; } } console.log(`总共获取到 ${allAssets.length} 个媒体文件`); // 4. 使用 reduce 进行统计,更高效 const stats = allAssets.reduce((acc, asset) => { acc.total++; switch (asset.mediaType) { case 'photo': acc.photos++; break; case 'video': acc.videos++; break; case 'audio': acc.audios++; break; default: acc.others++; break; } if (asset.creationTime) { const date = new Date(asset.creationTime); const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; acc.byMonth[monthKey] = (acc.byMonth[monthKey] || 0) + 1; } return acc; }, { total: 0, photos: 0, videos: 0, audios: 0, others: 0, byMonth: {}, }); setStats(stats); } catch (error) { console.error('获取媒体库统计信息失败:', error); PermissionService.show({ title: i18n.t('permission:title.error'), message: i18n.t('permission:message.getStatsFailed') }); } finally { setIsLoading(false); } }; // 时间范围选择器 const TimeRangeSelector = () => ( {(['day', 'week', 'month', 'all'] as TimeRange[]).map((range) => ( { setTimeRange(range); }} disabled={isLoading} > {{ day: '今天', week: '本周', month: '本月', all: '全部' }[range]} ))} ); return ( 媒体库统计 {isLoading ? ( ) : ( 获取媒体库统计 )} {stats && ( 按月统计 {Object.entries(stats.byMonth) .sort(([a], [b]) => b.localeCompare(a)) .map(([month, count]) => ( {month} {count} 个文件 ))} )} ); }; const StatItem = ({ label, value }: { label: string; value: string }) => ( {value} {label} ); 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;