198 lines
6.7 KiB
TypeScript
Executable File
198 lines
6.7 KiB
TypeScript
Executable File
import React, { useEffect, useState } from 'react';
|
||
import { Button, DatePicker, Form, message, Select } from 'antd';
|
||
import { CopyOutlined } from '@ant-design/icons';
|
||
import { useQueryClient } from '@tanstack/react-query';
|
||
import { getQueryKey } from '@trpc/react-query';
|
||
import { api } from '@nice/client';
|
||
import dayjs from 'dayjs';
|
||
import utc from 'dayjs/plugin/utc';
|
||
import timezone from 'dayjs/plugin/timezone';
|
||
import { ShareCodeResponse, useQuickFileContext } from '../quick-file/quickFileContext';
|
||
dayjs.extend(utc);
|
||
dayjs.extend(timezone);
|
||
interface ShareCodeGeneratorProps {
|
||
fileId: string;
|
||
onSuccess?: (code: string) => void;
|
||
fileName?: string;
|
||
}
|
||
|
||
|
||
export function copyToClipboard(text) {
|
||
if (navigator.clipboard) {
|
||
return navigator.clipboard.writeText(text);
|
||
} else {
|
||
const textarea = document.createElement('textarea');
|
||
textarea.value = text;
|
||
document.body.appendChild(textarea);
|
||
textarea.select();
|
||
document.execCommand('copy');
|
||
document.body.removeChild(textarea);
|
||
return Promise.resolve();
|
||
}
|
||
}
|
||
export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
|
||
fileId,
|
||
fileName,
|
||
}) => {
|
||
const [loading, setLoading] = useState(false);
|
||
const [shareCode, setShareCode] = useState<string>('');
|
||
const [expiresAt, setExpiresAt] = useState<string | null>(null);
|
||
const [canUseTimes, setCanUseTimes] = useState<number>(null);
|
||
const queryClient = useQueryClient();
|
||
const queryKey = getQueryKey(api.shareCode);
|
||
const [isGenerate, setIsGenerate] = useState(false);
|
||
const [currentFileId, setCurrentFileId] = useState<string>('');
|
||
const [form] = Form.useForm();
|
||
const { saveCodeRecord } = useQuickFileContext();
|
||
const generateShareCode = api.shareCode.generateShareCodeByFileId.useMutation({
|
||
onSuccess: (data) => {
|
||
queryClient.invalidateQueries({ queryKey });
|
||
},
|
||
});
|
||
useEffect(() => {
|
||
if (fileId !== currentFileId || !fileId) {
|
||
setIsGenerate(false);
|
||
}
|
||
setCurrentFileId(fileId);
|
||
}, [fileId])
|
||
const generateCode = async () => {
|
||
if (!fileId) {
|
||
message.error('请先上传文件');
|
||
return;
|
||
}
|
||
setLoading(true);
|
||
console.log('开始生成分享码,fileId:', fileId, 'fileName:', fileName);
|
||
try {
|
||
const data: ShareCodeResponse = await generateShareCode.mutateAsync({
|
||
fileId,
|
||
expiresAt: form.getFieldsValue()?.expiresAt ? form.getFieldsValue().expiresAt.tz('Asia/Shanghai').toDate() : dayjs().add(1, 'day').tz('Asia/Shanghai').toDate(),
|
||
canUseTimes: form.getFieldsValue()?.canUseTimes ? form.getFieldsValue().canUseTimes : 10,
|
||
});
|
||
console.log('data', data)
|
||
setShareCode(data.code);
|
||
setIsGenerate(true);
|
||
setExpiresAt(dayjs(data.expiresAt).format('YYYY-MM-DD HH:mm:ss'));
|
||
setCanUseTimes(data.canUseTimes);
|
||
saveCodeRecord(data,'shareCodeGeneratorRecords');
|
||
message.success('分享码生成成功'+data.code);
|
||
} catch (error) {
|
||
console.error('生成分享码错误:', error);
|
||
message.error('生成分享码失败: ' + (error instanceof Error ? error.message : '未知错误'));
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleCopy = (code) => {
|
||
copyToClipboard(code)
|
||
.then(() => console.log('复制成功'))
|
||
.catch(() => console.error('复制失败'));
|
||
};
|
||
useEffect(() => {
|
||
const date = dayjs().add(1, 'day').tz('Asia/Shanghai');
|
||
form.setFieldsValue({
|
||
expiresAt: date,
|
||
canUseTimes: 10
|
||
});
|
||
}, [form]);
|
||
|
||
useEffect(() => {
|
||
if (fileId) {
|
||
generateCode()
|
||
}
|
||
}, [fileId])
|
||
return (
|
||
<div style={{ padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '8px' }}>
|
||
<div style={{ marginBottom: '3px' }}>
|
||
<small style={{ color: '#666' }}>文件ID: {fileId ? fileId : '暂未上传文件'}</small>
|
||
</div>
|
||
{!isGenerate ? (
|
||
<>
|
||
<Form form={form}>
|
||
<div className='w-4/5 h-16 flex flex-row justify-between items-center'>
|
||
<small style={{ color: '#666' }}>
|
||
{"分享码的有效期"}
|
||
</small>
|
||
<Form.Item name="expiresAt" className='mt-5'>
|
||
<DatePicker
|
||
showTime
|
||
disabledDate={(current) => current && current < dayjs().startOf('day')}
|
||
disabledTime={(current) => {
|
||
if (current && current.isSame(dayjs(), 'day')) {
|
||
return {
|
||
disabledHours: () => [...Array(dayjs().hour() + 1).keys()],
|
||
disabledMinutes: (selectedHour) => {
|
||
if (selectedHour === dayjs().hour()) {
|
||
return [...Array(dayjs().minute() + 1).keys()];
|
||
}
|
||
return [];
|
||
}
|
||
};
|
||
}
|
||
return {};
|
||
}}
|
||
/>
|
||
</Form.Item>
|
||
<small style={{ color: '#666' }}>
|
||
{"分享码的使用次数"}
|
||
</small>
|
||
<Form.Item name="canUseTimes" className='mt-5'>
|
||
<Select
|
||
style={{ width: 120 }}
|
||
//onChange={handleChange}
|
||
options={[
|
||
{ value: 10, label: '10' },
|
||
{ value: 20, label: '20' },
|
||
{ value: 30, label: '30' },
|
||
{ value: 40, label: '40' },
|
||
{ value: 50, label: '50' },
|
||
]}
|
||
/>
|
||
</Form.Item>
|
||
</div>
|
||
</Form>
|
||
</>
|
||
|
||
) : (
|
||
<div style={{ textAlign: 'center' }}>
|
||
<div style={{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
gap: '12px',
|
||
margin: '16px 0'
|
||
}}>
|
||
<span style={{
|
||
fontSize: '24px',
|
||
fontWeight: 'bold',
|
||
letterSpacing: '2px',
|
||
color: '#1890ff',
|
||
padding: '8px 16px',
|
||
backgroundColor: '#e6f7ff',
|
||
borderRadius: '4px'
|
||
}}>
|
||
{shareCode}
|
||
</span>
|
||
<Button
|
||
icon={<CopyOutlined />}
|
||
onClick={() => {
|
||
handleCopy(shareCode)
|
||
//navigator.clipboard.writeText(shareCode);
|
||
message.success('分享码已复制');
|
||
}}
|
||
/>
|
||
</div>
|
||
{isGenerate && expiresAt ? (
|
||
<div style={{ color: '#666' }}>
|
||
有效期至: {expiresAt} 可使用次数: {canUseTimes}
|
||
</div>
|
||
) : (
|
||
<div style={{ color: 'red' }}>
|
||
未获取到有效期信息
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}; |