Files
web-bak/src/components/APKShareManager.tsx

285 lines
7.1 KiB
TypeScript
Raw Normal View History

2026-02-14 10:01:14 +08:00
import React, { useState, useEffect } from 'react'
import {
Card,
Button,
Table,
Typography,
Space,
message,
Tag,
Modal,
QRCode,
Tooltip,
Alert,
Row,
Col
} from 'antd'
import {
LinkOutlined,
CopyOutlined,
QrcodeOutlined,
CheckCircleOutlined,
ClockCircleOutlined
} from '@ant-design/icons'
import type { ColumnsType } from 'antd/es/table'
import apiClient from '../services/apiClient'
const { Text, Paragraph } = Typography
interface ShareInfo {
sessionId: string
filename: string
shareUrl: string
createdAt: string
expiresAt: string
isExpired: boolean
}
interface APKShareManagerProps {
serverUrl: string
onShareUrlGenerated?: (shareUrl: string) => void
}
const APKShareManager: React.FC<APKShareManagerProps> = ({
serverUrl,
onShareUrlGenerated
}) => {
const [shares, setShares] = useState<ShareInfo[]>([])
const [loading, setLoading] = useState(false)
const [qrModalVisible, setQrModalVisible] = useState(false)
const [currentShareUrl, setCurrentShareUrl] = useState('')
const [currentFilename, setCurrentFilename] = useState('')
// 获取分享链接列表
const fetchShares = async () => {
setLoading(true)
try {
const result = await apiClient.get<any>('/api/apk/shares')
if (result.success) {
setShares(result.shares || [])
// 如果有新的分享链接,回调通知
if (result.shares && result.shares.length > 0 && onShareUrlGenerated) {
const latestShare = result.shares[result.shares.length - 1]
onShareUrlGenerated(latestShare.shareUrl)
}
}
} catch (error) {
console.error('获取分享链接失败:', error)
message.error('获取分享链接失败')
} finally {
setLoading(false)
}
}
// 复制链接
const copyShareUrl = async (shareUrl: string) => {
try {
await navigator.clipboard.writeText(shareUrl)
message.success('分享链接已复制到剪贴板')
} catch (error) {
message.error('复制失败,请手动复制')
}
}
// 显示二维码
const showQRCode = (shareUrl: string, filename: string) => {
setCurrentShareUrl(shareUrl)
setCurrentFilename(filename)
setQrModalVisible(true)
}
// 格式化时间
const formatTime = (timeStr: string) => {
const date = new Date(timeStr)
return date.toLocaleString('zh-CN')
}
// 计算剩余时间
const getRemainingTime = (expiresAt: string) => {
const now = Date.now()
const expiry = new Date(expiresAt).getTime()
const remaining = expiry - now
if (remaining <= 0) {
return '已过期'
}
const minutes = Math.floor(remaining / 60000)
const seconds = Math.floor((remaining % 60000) / 1000)
if (minutes > 0) {
return `${minutes}${seconds}`
} else {
return `${seconds}`
}
}
// 表格列定义
const columns: ColumnsType<ShareInfo> = [
{
title: '文件名',
dataIndex: 'filename',
key: 'filename',
render: (filename) => (
<Space>
<Text strong>{filename}</Text>
</Space>
)
},
{
title: '状态',
key: 'status',
render: (_, record) => (
<Tag
icon={record.isExpired ? <ClockCircleOutlined /> : <CheckCircleOutlined />}
color={record.isExpired ? 'red' : 'green'}
>
{record.isExpired ? '已过期' : '活跃'}
</Tag>
)
},
{
title: '剩余时间',
key: 'remaining',
render: (_, record) => (
<Text type={record.isExpired ? 'danger' : 'success'}>
{getRemainingTime(record.expiresAt)}
</Text>
)
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
render: (createdAt) => formatTime(createdAt)
},
{
title: '过期时间',
dataIndex: 'expiresAt',
key: 'expiresAt',
render: (expiresAt) => formatTime(expiresAt)
},
{
title: '操作',
key: 'actions',
render: (_, record) => (
<Space>
<Tooltip title="显示二维码">
<Button
type="default"
size="small"
icon={<QrcodeOutlined />}
onClick={() => showQRCode(record.shareUrl, record.filename)}
disabled={record.isExpired}
/>
</Tooltip>
</Space>
)
}
]
// 组件挂载时获取数据
useEffect(() => {
fetchShares()
// 定时刷新分享列表
const interval = setInterval(fetchShares, 10000) // 每10秒刷新一次
return () => clearInterval(interval)
}, [serverUrl])
return (
<Card
title={
<Space>
<LinkOutlined />
<span>Cloudflare </span>
</Space>
}
extra={
<Button
type="primary"
onClick={fetchShares}
loading={loading}
>
</Button>
}
>
{shares.length === 0 ? (
<Alert
message="暂无分享链接"
description="构建APK后将自动生成Cloudflare分享链接有效期10分钟"
type="info"
showIcon
/>
) : (
<>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={24}>
<Alert
message="分享链接说明"
description={
<div>
<p> APK后会自动生成Cloudflare临时分享链接</p>
<p> 10</p>
<p> </p>
<p> </p>
</div>
}
type="info"
showIcon
/>
</Col>
</Row>
<Table
columns={columns}
dataSource={shares || []}
rowKey="sessionId"
loading={loading}
pagination={false}
size="small"
/>
</>
)}
{/* 二维码模态框 */}
<Modal
title={`${currentFilename} - 分享二维码`}
open={qrModalVisible}
onCancel={() => setQrModalVisible(false)}
footer={[
<Button key="copy" onClick={() => copyShareUrl(currentShareUrl)}>
<CopyOutlined />
</Button>,
<Button key="close" onClick={() => setQrModalVisible(false)}>
</Button>
]}
width={400}
>
<div style={{ textAlign: 'center', padding: '20px 0' }}>
<QRCode value={currentShareUrl} size={200} />
<Paragraph
copyable
style={{
marginTop: 16,
wordBreak: 'break-all',
fontSize: '12px'
}}
>
{currentShareUrl}
</Paragraph>
<Text type="secondary" style={{ fontSize: '12px' }}>
使APK
</Text>
</div>
</Modal>
</Card>
)
}
export default APKShareManager