172 lines
5.6 KiB
TypeScript
172 lines
5.6 KiB
TypeScript
import { useState } from "react"
|
||
import { Button } from "@/components/ui/button"
|
||
import {
|
||
Dialog,
|
||
DialogContent,
|
||
DialogDescription,
|
||
DialogFooter,
|
||
DialogHeader,
|
||
DialogTitle,
|
||
DialogTrigger,
|
||
} from "@/components/ui/dialog"
|
||
import { Download } from "lucide-react"
|
||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
|
||
import { Label } from "@/components/ui/label"
|
||
import { exportBatchCards } from "@/lib/api/card"
|
||
import { useToast } from "@/components/ui/use-toast"
|
||
import { isAuthenticated } from "@/lib/api/auth"
|
||
|
||
interface ExportBatchDialogProps {
|
||
batchId: number
|
||
}
|
||
|
||
export function ExportBatchDialog({ batchId }: ExportBatchDialogProps) {
|
||
const [open, setOpen] = useState(false)
|
||
const [isExporting, setIsExporting] = useState(false)
|
||
const [format, setFormat] = useState<'xlsx' | 'csv'>('xlsx')
|
||
const { toast } = useToast()
|
||
|
||
const handleExport = async () => {
|
||
try {
|
||
// 检查登录状态
|
||
if (!isAuthenticated()) {
|
||
toast({
|
||
title: "未登录",
|
||
description: "请先登录后再进行操作",
|
||
variant: "destructive",
|
||
})
|
||
return
|
||
}
|
||
|
||
setIsExporting(true)
|
||
const data = await exportBatchCards(batchId, format)
|
||
|
||
// 创建下载链接
|
||
const link = document.createElement('a')
|
||
const fileExtension = format
|
||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
||
const fileName = `batch-${batchId}-${timestamp}.${fileExtension}`
|
||
|
||
if (format === 'xlsx') {
|
||
// 处理二进制数据 (Excel)
|
||
const blob = new Blob([data as Blob], {
|
||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||
})
|
||
const url = URL.createObjectURL(blob)
|
||
link.href = url
|
||
link.download = fileName
|
||
document.body.appendChild(link)
|
||
link.click()
|
||
URL.revokeObjectURL(url)
|
||
document.body.removeChild(link)
|
||
} else {
|
||
// 处理文本数据 (CSV)
|
||
const blob = new Blob([data as string], { type: 'text/csv;charset=utf-8;' })
|
||
const url = URL.createObjectURL(blob)
|
||
link.href = url
|
||
link.download = fileName
|
||
document.body.appendChild(link)
|
||
link.click()
|
||
URL.revokeObjectURL(url)
|
||
document.body.removeChild(link)
|
||
}
|
||
|
||
toast({
|
||
title: "导出成功",
|
||
description: `批次 ${batchId} 已成功导出为 ${format.toUpperCase()} 格式`,
|
||
})
|
||
|
||
setOpen(false)
|
||
} catch (error) {
|
||
console.error("导出失败:", error)
|
||
|
||
// 处理不同类型的错误
|
||
let errorMessage = "无法导出批次,请稍后重试"
|
||
|
||
if (error instanceof Error) {
|
||
errorMessage = error.message
|
||
} else if (typeof error === 'object' && error !== null && 'response' in error) {
|
||
// Axios 错误
|
||
const axiosError = error as any
|
||
if (axiosError.response?.status === 401) {
|
||
errorMessage = "认证失败,请重新登录"
|
||
} else if (axiosError.response?.data?.message) {
|
||
errorMessage = axiosError.response.data.message
|
||
} else if (axiosError.response?.statusText) {
|
||
errorMessage = `请求失败: ${axiosError.response.statusText}`
|
||
}
|
||
}
|
||
|
||
toast({
|
||
title: "导出失败",
|
||
description: errorMessage,
|
||
variant: "destructive",
|
||
})
|
||
} finally {
|
||
setIsExporting(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<Dialog open={open} onOpenChange={setOpen}>
|
||
<DialogTrigger asChild>
|
||
<Button
|
||
variant="ghost"
|
||
size="sm"
|
||
className="h-8 hover:bg-blue-50 hover:text-blue-600"
|
||
>
|
||
<Download className="h-4 w-4 mr-1" />
|
||
导出
|
||
</Button>
|
||
</DialogTrigger>
|
||
<DialogContent className="sm:max-w-[425px]">
|
||
<DialogHeader>
|
||
<DialogTitle>导出批次数据</DialogTitle>
|
||
<DialogDescription>
|
||
选择导出格式并下载批次 {batchId} 的卡片数据
|
||
</DialogDescription>
|
||
</DialogHeader>
|
||
<div className="py-4">
|
||
<RadioGroup
|
||
value={format}
|
||
onValueChange={(value) => setFormat(value as 'xlsx' | 'csv')}
|
||
className="flex flex-col space-y-3"
|
||
>
|
||
<div className="flex items-center space-x-2">
|
||
<RadioGroupItem value="xlsx" id="xlsx" />
|
||
<Label htmlFor="xlsx" className="font-medium">Excel (XLSX)</Label>
|
||
<span className="text-xs text-gray-500 ml-1">- 包含格式的电子表格</span>
|
||
</div>
|
||
<div className="flex items-center space-x-2">
|
||
<RadioGroupItem value="csv" id="csv" />
|
||
<Label htmlFor="csv" className="font-medium">CSV</Label>
|
||
<span className="text-xs text-gray-500 ml-1">- 通用文本格式,兼容所有软件</span>
|
||
</div>
|
||
</RadioGroup>
|
||
</div>
|
||
<DialogFooter>
|
||
<Button variant="outline" onClick={() => setOpen(false)}>
|
||
取消
|
||
</Button>
|
||
<Button
|
||
onClick={handleExport}
|
||
className="bg-gradient-to-r from-blue-500 to-blue-600"
|
||
disabled={isExporting}
|
||
>
|
||
{isExporting ? (
|
||
<>
|
||
<div className="h-4 w-4 mr-2 animate-spin rounded-full border-2 border-current border-t-transparent" />
|
||
导出中...
|
||
</>
|
||
) : (
|
||
<>
|
||
<Download className="h-4 w-4 mr-2" />
|
||
导出 {format.toUpperCase()}
|
||
</>
|
||
)}
|
||
</Button>
|
||
</DialogFooter>
|
||
</DialogContent>
|
||
</Dialog>
|
||
)
|
||
}
|