From edf38aad48f50fca78be3c47b1e91ca90bcfb156 Mon Sep 17 00:00:00 2001 From: aits2026 Date: Fri, 6 Mar 2026 16:16:34 +0800 Subject: [PATCH] refactor(sns): unify export date range dialog --- src/pages/SnsPage.scss | 38 +++++++++ src/pages/SnsPage.tsx | 188 ++++++++++------------------------------- 2 files changed, 81 insertions(+), 145 deletions(-) diff --git a/src/pages/SnsPage.scss b/src/pages/SnsPage.scss index 3fceb5b..d6d24ad 100644 --- a/src/pages/SnsPage.scss +++ b/src/pages/SnsPage.scss @@ -1705,6 +1705,44 @@ gap: 8px; } +.export-section-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.time-range-trigger.sns-export-time-range-trigger { + border: 1px solid var(--border-color); + background: var(--bg-primary); + border-radius: 999px; + color: var(--text-primary); + font-size: 12px; + min-height: 32px; + padding: 0 10px; + display: inline-flex; + align-items: center; + gap: 8px; + cursor: pointer; + transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease; + + &:hover:not(:disabled) { + border-color: rgba(var(--primary-rgb), 0.45); + color: var(--primary); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .time-range-arrow { + color: var(--text-tertiary); + font-weight: 700; + line-height: 1; + } +} + .export-format-options { display: grid; diff --git a/src/pages/SnsPage.tsx b/src/pages/SnsPage.tsx index 3e296cc..0482240 100644 --- a/src/pages/SnsPage.tsx +++ b/src/pages/SnsPage.tsx @@ -1,5 +1,5 @@ import { useEffect, useLayoutEffect, useState, useRef, useCallback, useMemo } from 'react' -import { RefreshCw, Search, X, Download, FolderOpen, FileJson, FileText, Image, CheckCircle, AlertCircle, Calendar, Info, ChevronLeft, ChevronRight, Shield, ShieldOff, Loader2 } from 'lucide-react' +import { RefreshCw, Search, X, Download, FolderOpen, FileJson, FileText, Image, CheckCircle, AlertCircle, Calendar, Info, Shield, ShieldOff, Loader2 } from 'lucide-react' import './SnsPage.scss' import { SnsPost } from '../types/sns' import { SnsPostItem } from '../components/Sns/SnsPostItem' @@ -7,7 +7,13 @@ import { SnsFilterPanel } from '../components/Sns/SnsFilterPanel' import { ContactSnsTimelineDialog } from '../components/Sns/ContactSnsTimelineDialog' import type { ContactSnsTimelineTarget } from '../components/Sns/contactSnsTimeline' import JumpToDatePopover from '../components/JumpToDatePopover' +import { ExportDateRangeDialog } from '../components/Export/ExportDateRangeDialog' import * as configService from '../services/config' +import { + createExportDateRangeSelectionFromPreset, + getExportDateRangeLabel, + type ExportDateRangeSelection +} from '../utils/exportDateRange' const SNS_PAGE_CACHE_TTL_MS = 24 * 60 * 60 * 1000 const SNS_PAGE_CACHE_POST_LIMIT = 200 @@ -133,13 +139,14 @@ export default function SnsPage() { const [exportImages, setExportImages] = useState(false) const [exportLivePhotos, setExportLivePhotos] = useState(false) const [exportVideos, setExportVideos] = useState(false) - const [exportDateRange, setExportDateRange] = useState<{ start: string; end: string }>({ start: '', end: '' }) + const [exportDateRangeSelection, setExportDateRangeSelection] = useState( + () => createExportDateRangeSelectionFromPreset('all') + ) const [isExporting, setIsExporting] = useState(false) const [exportProgress, setExportProgress] = useState<{ current: number; total: number; status: string } | null>(null) const [exportResult, setExportResult] = useState<{ success: boolean; filePath?: string; postCount?: number; mediaCount?: number; error?: string } | null>(null) const [refreshSpin, setRefreshSpin] = useState(false) - const [calendarPicker, setCalendarPicker] = useState<{ field: 'start' | 'end'; month: Date } | null>(null) - const [showYearMonthPicker, setShowYearMonthPicker] = useState(false) + const [isExportDateRangeDialogOpen, setIsExportDateRangeDialogOpen] = useState(false) // 触发器相关状态 const [showTriggerDialog, setShowTriggerDialog] = useState(false) @@ -546,6 +553,8 @@ export default function SnsPage() { return `${formatDateOnly(overviewStats.earliestTime)} ~ ${formatDateOnly(overviewStats.latestTime)}` } + const exportDateRangeLabel = useMemo(() => getExportDateRangeLabel(exportDateRangeSelection), [exportDateRangeSelection]) + const loadPosts = useCallback(async (options: { reset?: boolean, direction?: 'older' | 'newer' } = {}) => { const { reset = false, direction = 'older' } = options if (loadingRef.current) return @@ -1180,7 +1189,8 @@ export default function SnsPage() { onClick={() => { setExportResult(null) setExportProgress(null) - setExportDateRange({ start: '', end: '' }) + setExportDateRangeSelection(createExportDateRangeSelectionFromPreset('all')) + setIsExportDateRangeDialogOpen(false) setShowExportDialog(true) }} className="icon-btn export-btn" @@ -1505,31 +1515,19 @@ export default function SnsPage() { {/* 时间范围 */}
- -
-
{ - if (!isExporting) setCalendarPicker(prev => prev?.field === 'start' ? null : { field: 'start', month: exportDateRange.start ? new Date(exportDateRange.start) : new Date() }) - }}> - - - {exportDateRange.start || '开始日期'} - - {exportDateRange.start && ( - { e.stopPropagation(); setExportDateRange(prev => ({ ...prev, start: '' })) }} /> - )} -
- -
{ - if (!isExporting) setCalendarPicker(prev => prev?.field === 'end' ? null : { field: 'end', month: exportDateRange.end ? new Date(exportDateRange.end) : new Date() }) - }}> - - - {exportDateRange.end || '结束日期'} - - {exportDateRange.end && ( - { e.stopPropagation(); setExportDateRange(prev => ({ ...prev, end: '' })) }} /> - )} -
+
+ +
@@ -1620,8 +1618,12 @@ export default function SnsPage() { exportImages, exportLivePhotos, exportVideos, - startTime: exportDateRange.start ? Math.floor(new Date(exportDateRange.start).getTime() / 1000) : undefined, - endTime: exportDateRange.end ? Math.floor(new Date(exportDateRange.end + 'T23:59:59').getTime() / 1000) : undefined + startTime: exportDateRangeSelection.useAllTime + ? undefined + : Math.floor(exportDateRangeSelection.dateRange.start.getTime() / 1000), + endTime: exportDateRangeSelection.useAllTime + ? undefined + : Math.floor(exportDateRangeSelection.dateRange.end.getTime() / 1000) }) setExportResult(result) } catch (e: any) { @@ -1688,119 +1690,15 @@ export default function SnsPage() {
)} - {/* 日期选择弹窗 */} - {calendarPicker && ( -
{ setCalendarPicker(null); setShowYearMonthPicker(false) }}> -
e.stopPropagation()}> -
-
- -

选择{calendarPicker.field === 'start' ? '开始' : '结束'}日期

-
- -
-
-
- - setShowYearMonthPicker(!showYearMonthPicker)}> - {calendarPicker.month.getFullYear()}年{calendarPicker.month.getMonth() + 1}月 - - -
- {showYearMonthPicker ? ( -
-
- - {calendarPicker.month.getFullYear()}年 - -
-
- {['一月','二月','三月','四月','五月','六月','七月','八月','九月','十月','十一月','十二月'].map((name, i) => ( - - ))} -
-
- ) : ( - <> -
- {['日', '一', '二', '三', '四', '五', '六'].map(d =>
{d}
)} -
-
- {(() => { - const y = calendarPicker.month.getFullYear() - const m = calendarPicker.month.getMonth() - const firstDay = new Date(y, m, 1).getDay() - const daysInMonth = new Date(y, m + 1, 0).getDate() - const cells: (number | null)[] = [] - for (let i = 0; i < firstDay; i++) cells.push(null) - for (let i = 1; i <= daysInMonth; i++) cells.push(i) - const today = new Date() - return cells.map((day, i) => { - if (day === null) return
- const dateStr = `${y}-${String(m + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}` - const isToday = day === today.getDate() && m === today.getMonth() && y === today.getFullYear() - const currentVal = calendarPicker.field === 'start' ? exportDateRange.start : exportDateRange.end - const isSelected = dateStr === currentVal - return ( -
{ - setExportDateRange(prev => ({ ...prev, [calendarPicker.field]: dateStr })) - setCalendarPicker(null) - }} - >{day}
- ) - }) - })()} -
- - )} -
-
- - -
-
- -
-
-
- )} + setIsExportDateRangeDialogOpen(false)} + onConfirm={(nextSelection) => { + setExportDateRangeSelection(nextSelection) + setIsExportDateRangeDialogOpen(false) + }} + />
) }