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

285 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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