聊天页面新增导出按钮

This commit is contained in:
xuncha
2026-02-06 23:37:50 +08:00
parent e56ee1ff4a
commit 1aab8dfc4e
2 changed files with 56 additions and 3 deletions

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'
import { Search, MessageSquare, AlertCircle, Loader2, RefreshCw, X, ChevronDown, Info, Calendar, Database, Hash, Play, Pause, Image as ImageIcon, Link, Mic, CheckCircle, Copy, Check } from 'lucide-react'
import { Search, MessageSquare, AlertCircle, Loader2, RefreshCw, X, ChevronDown, Info, Calendar, Database, Hash, Play, Pause, Image as ImageIcon, Link, Mic, CheckCircle, Copy, Check, Download } from 'lucide-react'
import { useNavigate } from 'react-router-dom'
import { createPortal } from 'react-dom'
import { useChatStore } from '../stores/chatStore'
import { useBatchTranscribeStore } from '../stores/batchTranscribeStore'
@@ -117,6 +118,8 @@ const SessionItem = React.memo(function SessionItem({
function ChatPage(_props: ChatPageProps) {
const navigate = useNavigate()
const {
isConnected,
isConnecting,
@@ -1228,7 +1231,7 @@ function ChatPage(_props: ChatPageProps) {
return
}
const voiceMessages = result.messages
const voiceMessages: Message[] = result.messages
if (voiceMessages.length === 0) {
alert('当前会话没有语音消息')
return
@@ -1245,6 +1248,15 @@ function ChatPage(_props: ChatPageProps) {
setShowBatchConfirm(true)
}, [sessions, currentSessionId, isBatchTranscribing])
const handleExportCurrentSession = useCallback(() => {
if (!currentSessionId) return
navigate('/export', {
state: {
preselectSessionIds: [currentSessionId]
}
})
}, [currentSessionId, navigate])
// 确认批量转写
const confirmBatchTranscribe = useCallback(async () => {
if (!currentSessionId) return
@@ -1465,6 +1477,14 @@ function ChatPage(_props: ChatPageProps) {
)}
</div>
<div className="header-actions">
<button
className="icon-btn export-session-btn"
onClick={handleExportCurrentSession}
disabled={!currentSessionId}
title="导出当前会话"
>
<Download size={18} />
</button>
<button
className={`icon-btn batch-transcribe-btn${isBatchTranscribing ? ' transcribing' : ''}`}
onClick={() => {

View File

@@ -1,4 +1,5 @@
import { useState, useEffect, useCallback, useRef } from 'react'
import { useState, useEffect, useCallback, useRef, useMemo } from 'react'
import { useLocation } from 'react-router-dom'
import { Search, Download, FolderOpen, RefreshCw, Check, Calendar, FileJson, FileText, Table, Loader2, X, ChevronDown, ChevronLeft, ChevronRight, FileSpreadsheet, Database, FileCode, CheckCircle, XCircle, ExternalLink } from 'lucide-react'
import * as configService from '../services/config'
import './ExportPage.scss'
@@ -38,6 +39,7 @@ interface ExportResult {
type SessionLayout = 'shared' | 'per-session'
function ExportPage() {
const location = useLocation()
const defaultTxtColumns = ['index', 'time', 'senderRole', 'messageType', 'content']
const [sessions, setSessions] = useState<ChatSession[]>([])
const [filteredSessions, setFilteredSessions] = useState<ChatSession[]>([])
@@ -63,6 +65,19 @@ function ExportPage() {
const exportStartTime = useRef<number>(0)
const [elapsedSeconds, setElapsedSeconds] = useState(0)
const displayNameDropdownRef = useRef<HTMLDivElement>(null)
const preselectAppliedRef = useRef(false)
const preselectSessionIds = useMemo(() => {
const state = location.state as { preselectSessionIds?: unknown; preselectSessionId?: unknown } | null
const rawList = Array.isArray(state?.preselectSessionIds)
? state?.preselectSessionIds
: (typeof state?.preselectSessionId === 'string' ? [state.preselectSessionId] : [])
return rawList
.filter((item): item is string => typeof item === 'string')
.map(item => item.trim())
.filter(Boolean)
}, [location.state])
const [options, setOptions] = useState<ExportOptions>({
format: 'excel',
@@ -184,6 +199,24 @@ function ExportPage() {
loadExportDefaults()
}, [loadSessions, loadExportPath, loadExportDefaults])
useEffect(() => {
preselectAppliedRef.current = false
}, [location.key, preselectSessionIds])
useEffect(() => {
if (preselectAppliedRef.current) return
if (sessions.length === 0 || preselectSessionIds.length === 0) return
const exists = new Set(sessions.map(session => session.username))
const matched = preselectSessionIds.filter(id => exists.has(id))
preselectAppliedRef.current = true
if (matched.length > 0) {
setSelectedSessions(new Set(matched))
setSearchKeyword('')
}
}, [sessions, preselectSessionIds])
useEffect(() => {
const handleChange = () => {
setSelectedSessions(new Set())