rht
This commit is contained in:
parent
9edce44b32
commit
34dde48aaf
|
@ -9,6 +9,7 @@ import dayjs from 'dayjs';
|
|||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import { BaseService } from '../base/base.service';
|
||||
import { url } from 'inspector';
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
export interface ShareCode {
|
||||
|
@ -25,12 +26,19 @@ export interface GenerateShareCodeResponse {
|
|||
code: string;
|
||||
expiresAt: Date;
|
||||
canUseTimes: number;
|
||||
fileName?: string;
|
||||
resource: {
|
||||
id: string;
|
||||
type: string;
|
||||
url: string;
|
||||
meta: ResourceMeta
|
||||
}
|
||||
}
|
||||
|
||||
interface ResourceMeta {
|
||||
filename: string;
|
||||
filetype: string;
|
||||
filesize: string;
|
||||
size: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
@ -61,7 +69,7 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
|||
if (!resource) {
|
||||
throw new NotFoundException('文件不存在');
|
||||
}
|
||||
const { filename } = resource.meta as any as ResourceMeta
|
||||
const { filename, filetype, size } = resource.meta as any as ResourceMeta
|
||||
// 生成分享码
|
||||
const code = this.generateCode();
|
||||
// 查找是否已有分享码记录
|
||||
|
@ -78,7 +86,7 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
|||
expiresAt,
|
||||
canUseTimes,
|
||||
isUsed: false,
|
||||
fileName: filename|| "downloaded_file",
|
||||
fileName: filename || "downloaded_file",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
|
@ -99,6 +107,17 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
|||
code,
|
||||
expiresAt,
|
||||
canUseTimes,
|
||||
fileName: filename || "downloaded_file",
|
||||
resource: {
|
||||
id: resource.id,
|
||||
type: resource.type,
|
||||
url: resource.url,
|
||||
meta: {
|
||||
filename,
|
||||
filetype,
|
||||
size,
|
||||
}
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to generate share code', error);
|
||||
|
@ -274,7 +293,7 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
|||
where: { fileId: shareCode.fileId },
|
||||
});
|
||||
this.logger.log('获取到的资源信息:', resource);
|
||||
const { filename } = resource.meta as any as ResourceMeta
|
||||
const { filename,filetype,size } = resource.meta as any as ResourceMeta
|
||||
const fileUrl = resource?.url
|
||||
if (!resource) {
|
||||
throw new NotFoundException('文件不存在');
|
||||
|
@ -282,12 +301,19 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
|||
|
||||
// 直接返回正确的数据结构
|
||||
const response = {
|
||||
fileId: shareCode.fileId,
|
||||
fileName: filename || 'downloaded_file',
|
||||
id:shareCode.id,
|
||||
code: shareCode.code,
|
||||
fileName: filename || 'downloaded_file',
|
||||
expiresAt: shareCode.expiresAt,
|
||||
url: fileUrl,
|
||||
canUseTimes: shareCode.canUseTimes - 1,
|
||||
resource:{
|
||||
id:resource.id,
|
||||
type:resource.type,
|
||||
url:resource.url,
|
||||
meta:{
|
||||
filename,filetype,size
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.logger.log('返回给前端的数据:', response); // 添加日志
|
||||
|
@ -316,11 +342,10 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
|||
totalPages: number;
|
||||
}> {
|
||||
try {
|
||||
console.log('args:', args.where.OR);
|
||||
// 使用include直接关联查询Resource
|
||||
const { items, totalPages } = await super.findManyWithPagination({
|
||||
...args,
|
||||
select:{
|
||||
select: {
|
||||
id: true,
|
||||
code: true,
|
||||
fileId: true,
|
||||
|
|
|
@ -3,6 +3,7 @@ import { api } from "@nice/client";
|
|||
import { createContext, useContext, useState } from "react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { getQueryKey } from "@trpc/react-query";
|
||||
import { ShareCodeResponse } from "../quick-file/quickFileContext";
|
||||
|
||||
interface CodeManageContextType {
|
||||
editForm: FormInstance<any>;
|
||||
|
@ -22,21 +23,7 @@ interface CodeManageContextType {
|
|||
}
|
||||
|
||||
interface ShareCodeWithResource {
|
||||
items: {
|
||||
id: string;
|
||||
code: string;
|
||||
fileName: string;
|
||||
fileSize: number;
|
||||
expiresAt: string;
|
||||
createdAt: string;
|
||||
canUseTimes: number;
|
||||
resource: {
|
||||
id: string;
|
||||
type: string;
|
||||
url: string;
|
||||
meta: any;
|
||||
}
|
||||
}[],
|
||||
items: ShareCodeResponse[],
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
|
|
|
@ -4,29 +4,14 @@ import dayjs from 'dayjs';
|
|||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import ShareCodeListCard from './ShareCodeListCard';
|
||||
import { ShareCodeResponse } from '../../quick-file/quickFileContext';
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
export interface ShareCodeItem {
|
||||
id: string;
|
||||
code: string;
|
||||
expiresAt: string;
|
||||
fileName: string;
|
||||
canUseTimes: number;
|
||||
resource: {
|
||||
id: string;
|
||||
type: string;
|
||||
url: string;
|
||||
meta: {
|
||||
size: number;
|
||||
filename: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface ShareCodeListProps {
|
||||
data: ShareCodeItem[];
|
||||
data: ShareCodeResponse[];
|
||||
loading?: boolean;
|
||||
onEdit?: (id: string,expiresAt:Date,canUseTimes:number,code:string) => void;
|
||||
onDelete?: (id: string) => void;
|
||||
|
@ -46,7 +31,7 @@ const ShareCodeList: React.FC<ShareCodeListProps> = ({
|
|||
loading={loading}
|
||||
renderItem={(item) => (
|
||||
<List.Item>
|
||||
<ShareCodeListCard item={item} onEdit={onEdit} onDelete={onDelete} />
|
||||
<ShareCodeListCard item={item} onEdit={onEdit} onDelete={onDelete} styles='w-[262px]' />
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
import { DeleteOutlined, EditOutlined } from "@ant-design/icons";
|
||||
import { DeleteOutlined, DownloadOutlined, EditOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, Typography } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import { ShareCodeItem } from "./ShareCodeList";
|
||||
import { useEffect } from "react";
|
||||
import { ShareCodeResponse } from "../../quick-file/quickFileContext";
|
||||
|
||||
export default function ShareCodeListCard({ item, onEdit, onDelete }: { item: ShareCodeItem, onEdit: (id: string,expiresAt:Date,canUseTimes:number,code:string) => void, onDelete: (id: string) => void }) {
|
||||
export default function ShareCodeListCard({ item, onEdit, onDelete, styles, onDownload }:
|
||||
{
|
||||
item: ShareCodeResponse,
|
||||
styles?: string,
|
||||
onDelete: (id: string) => void,
|
||||
onEdit?: (id: string, expiresAt: Date, canUseTimes: number, code: string) => void,
|
||||
onDownload?: (id: string) => void
|
||||
}) {
|
||||
useEffect(() => {
|
||||
console.log('item:', item);
|
||||
}, [item]);
|
||||
return <div>
|
||||
return <div className={`${styles}`}>
|
||||
<Card
|
||||
className="shadow-md hover:shadow-lg transition-shadow duration-300 space-x-4"
|
||||
title={
|
||||
|
@ -21,28 +28,38 @@ export default function ShareCodeListCard({ item, onEdit, onDelete }: { item: Sh
|
|||
}
|
||||
hoverable
|
||||
actions={[
|
||||
onEdit && (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => onEdit?.(item.id,dayjs(item.expiresAt).toDate(),item.canUseTimes,item.code)}
|
||||
onClick={() => onEdit?.(item.id, dayjs(item.expiresAt).toDate(), item.canUseTimes, item.code)}
|
||||
className="text-blue-500 hover:text-blue-700"
|
||||
/>,
|
||||
/>
|
||||
),
|
||||
onDownload && (
|
||||
<Button
|
||||
type="text"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => onDownload?.(item.code)}
|
||||
className="text-blue-500 hover:text-blue-700"
|
||||
/>
|
||||
),
|
||||
<Button
|
||||
type="text"
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => onDelete?.(item.id)}
|
||||
className="text-red-500 hover:text-red-700"
|
||||
/>
|
||||
]}
|
||||
].filter(Boolean)}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<p className="text-gray-700">
|
||||
<span className="font-medium">文件名:</span> {item.fileName}
|
||||
</p>
|
||||
<p className="text-gray-700">
|
||||
<span className="font-medium">文件大小:</span> {Math.max(0.01, (item?.resource?.meta?.size / 1024 / 1024)).toFixed(2)} MB
|
||||
<span className="font-medium">文件大小:</span> {Math.max(0.01, (Number(item?.resource?.meta?.size) / 1024 / 1024)).toFixed(2)} MB
|
||||
</p>
|
||||
<p className="text-gray-700">
|
||||
<p className="text-gray-700 ">
|
||||
<span className="font-medium">过期时间:</span> {dayjs(item.expiresAt).format('YYYY-MM-DD HH:mm:ss')}
|
||||
</p>
|
||||
<p className="text-gray-700">
|
||||
|
|
|
@ -5,47 +5,26 @@ import { message, Tabs, Form, Spin, Alert } from "antd";
|
|||
import { env } from '../../../env'
|
||||
import { TusUploader } from "@web/src/components/common/uploader/TusUploader";
|
||||
import { useState } from "react";
|
||||
import CodeRecord from "../sharecode/components/CodeRecord";
|
||||
const { TabPane } = Tabs;
|
||||
export default function QuickUploadPage() {
|
||||
const [form] = Form.useForm();
|
||||
const uploadFileId = Form.useWatch(["file"], form)?.[0]
|
||||
const [isGetingFileId, setIsGetingFileId] = useState(false);
|
||||
const handleShareSuccess = (code: string) => {
|
||||
message.success('分享码生成成功:' + code);
|
||||
}
|
||||
const handleValidSuccess = async (fileUrl: string, fileName: string) => {
|
||||
setIsGetingFileId(true);
|
||||
try {
|
||||
const downloadUrl = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${fileUrl}`;
|
||||
const link = document.createElement('a');
|
||||
link.href = downloadUrl;
|
||||
link.download = fileName;
|
||||
link.target = '_blank';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
message.success('文件下载开始');
|
||||
} catch (error) {
|
||||
console.error('下载失败:', error);
|
||||
message.error('文件下载失败');
|
||||
} finally {
|
||||
setIsGetingFileId(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
isGetingFileId ?
|
||||
(<Spin spinning={isGetingFileId} fullscreen />) :
|
||||
(<div style={{ maxWidth: '1000px', margin: '0 auto', padding: '20px' }}>
|
||||
<div style={{ maxWidth: '1000px', margin: '0 auto', padding: '20px' }}>
|
||||
<div className="flex items-center">
|
||||
<img src="/logo.svg" className="h-16 w-16 rounded-xl" />
|
||||
<span className="text-4xl py-4 mx-4">烽火快传</span>
|
||||
</div>
|
||||
<Tabs defaultActiveKey="upload">
|
||||
<TabPane tab="上传分享" key="upload">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<span className="text-lg text-zinc-700">上传文件并生成分享码</span>
|
||||
<CodeRecord title="我生成的分享码" btnContent="上传记录" recordName="shareCodeGeneratorRecords" />
|
||||
</div>
|
||||
<div>
|
||||
<Form form={form}>
|
||||
<Form.Item name="file">
|
||||
|
@ -59,22 +38,20 @@ export default function QuickUploadPage() {
|
|||
<div style={{ marginBottom: '40px' }}>
|
||||
<ShareCodeGenerator
|
||||
fileId={uploadFileId}
|
||||
onSuccess={handleShareSuccess}
|
||||
/>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane tab="下载文件" key="download">
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<span className="text-lg block text-zinc-700 py-4">使用分享码下载文件</span>
|
||||
<ShareCodeValidator
|
||||
onValidSuccess={handleValidSuccess}
|
||||
/>
|
||||
<CodeRecord title="我使用的分享码" btnContent="使用记录" recordName="shareCodeDownloadRecords" isDownload={true}/>
|
||||
</div>
|
||||
<div>
|
||||
<ShareCodeValidator />
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>)
|
||||
}
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
import { message } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import { createContext, useContext, useState } from "react";
|
||||
import { env } from "@web/src/env";
|
||||
import { api } from "@nice/client";
|
||||
interface QuickFileContextType {
|
||||
saveCodeRecord: (data: ShareCodeResponse, recordName: string) => void;
|
||||
handleValidSuccess: (fileUrl: string, fileName: string) => void;
|
||||
isGetingFileId: boolean;
|
||||
downloadCode: string | null;
|
||||
setDownloadCode: (code: string | null) => void;
|
||||
refetchShareCodeWithResource: () => Promise<{ data: any }>;
|
||||
isLoading: boolean;
|
||||
downloadResult: ShareCodeResponse | null;
|
||||
}
|
||||
export interface ShareCodeResponse {
|
||||
id?: string;
|
||||
code?: string;
|
||||
fileName?: string;
|
||||
expiresAt?: Date;
|
||||
canUseTimes?: number;
|
||||
resource?: {
|
||||
id: string;
|
||||
type: string;
|
||||
url: string;
|
||||
meta: ResourceMeta
|
||||
}
|
||||
}
|
||||
interface ResourceMeta {
|
||||
filename: string;
|
||||
filetype: string;
|
||||
size: string;
|
||||
}
|
||||
|
||||
export const QuickFileContext = createContext<QuickFileContextType | null>(null);
|
||||
|
||||
export const QuickFileProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [isGetingFileId, setIsGetingFileId] = useState(false);
|
||||
const [downloadCode, setDownloadCode] = useState<string | null>(null);
|
||||
const saveCodeRecord = (data: ShareCodeResponse, recordName: string) => {
|
||||
const newRecord = {
|
||||
id: `${Date.now()}`, // 生成唯一ID
|
||||
code: data.code,
|
||||
expiresAt: dayjs(data.expiresAt).format('YYYY-MM-DD HH:mm:ss'),
|
||||
fileName: data.fileName || `文件_${data.code}`,
|
||||
canUseTimes: data.canUseTimes,
|
||||
resource: {
|
||||
id: data.resource.id,
|
||||
type: data.resource.type,
|
||||
url: data.resource.url,
|
||||
meta: {
|
||||
size: data.resource.meta.size,
|
||||
filename: data.resource.meta.filename,
|
||||
filetype: data.resource.meta.filetype
|
||||
}
|
||||
}
|
||||
};
|
||||
// 获取已有记录并添加新记录
|
||||
const existingGeneratorRecords = localStorage.getItem(recordName);
|
||||
const generatorRecords = existingGeneratorRecords ? JSON.parse(existingGeneratorRecords) : [];
|
||||
generatorRecords.unshift(newRecord); // 添加到最前面
|
||||
localStorage.setItem(recordName, JSON.stringify(generatorRecords));
|
||||
}
|
||||
const handleValidSuccess = async (fileUrl: string, fileName: string) => {
|
||||
setIsGetingFileId(true);
|
||||
try {
|
||||
const downloadUrl = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${fileUrl}`;
|
||||
const link = document.createElement('a');
|
||||
link.href = downloadUrl;
|
||||
link.download = fileName;
|
||||
link.target = '_blank';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
message.success('文件下载开始');
|
||||
} catch (error) {
|
||||
console.error('下载失败:', error);
|
||||
message.error('文件下载失败');
|
||||
} finally {
|
||||
setIsGetingFileId(false);
|
||||
}
|
||||
};
|
||||
const { data: downloadResult, isLoading, refetch: refetchShareCodeWithResource } = api.shareCode.getFileByShareCode.useQuery(
|
||||
{ code: downloadCode?.trim() },
|
||||
{
|
||||
enabled: false
|
||||
}
|
||||
)
|
||||
return <>
|
||||
<QuickFileContext.Provider value={{
|
||||
saveCodeRecord,
|
||||
handleValidSuccess,
|
||||
isGetingFileId,
|
||||
downloadCode,
|
||||
setDownloadCode,
|
||||
refetchShareCodeWithResource,
|
||||
isLoading,
|
||||
downloadResult: downloadResult as any as ShareCodeResponse
|
||||
}}>
|
||||
{children}
|
||||
</QuickFileContext.Provider>
|
||||
</>
|
||||
};
|
||||
|
||||
export const useQuickFileContext = () => {
|
||||
const context = useContext(QuickFileContext);
|
||||
if (!context) {
|
||||
throw new Error("useQuickFileContext must be used within a QuickFileProvider");
|
||||
}
|
||||
return context;
|
||||
};
|
|
@ -0,0 +1,88 @@
|
|||
import { useState, useEffect } from "react";
|
||||
import { Button, Drawer, Empty } from "antd";
|
||||
import { HistoryOutlined } from "@ant-design/icons";
|
||||
import ShareCodeListCard from "../../code-manage/components/ShareCodeListCard";
|
||||
import { ShareCodeResponse, useQuickFileContext } from "../../quick-file/quickFileContext";
|
||||
|
||||
export default function CodeRecord({ title, btnContent, recordName ,styles,isDownload}:
|
||||
{ title: string, btnContent: string , recordName: string, styles?:string,isDownload?:boolean}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [records, setRecords] = useState<ShareCodeResponse[]>([]);
|
||||
const {handleValidSuccess,saveCodeRecord,refetchShareCodeWithResource,setDownloadCode} = useQuickFileContext();
|
||||
|
||||
const loadRecordsFromStorage = () => {
|
||||
const storedRecords = localStorage.getItem(recordName);
|
||||
if (storedRecords) {
|
||||
setRecords(JSON.parse(storedRecords));
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadRecordsFromStorage();
|
||||
}, [recordName]);
|
||||
|
||||
const saveAndUpdateRecord = (data: ShareCodeResponse, name: string) => {
|
||||
saveCodeRecord(data, name);
|
||||
if (name === recordName) {
|
||||
loadRecordsFromStorage();
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpen = () => {
|
||||
loadRecordsFromStorage();
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleDelete = (id: string) => {
|
||||
const updatedRecords = records.filter(record => record.id !== id);
|
||||
setRecords(updatedRecords);
|
||||
localStorage.setItem(recordName, JSON.stringify(updatedRecords));
|
||||
};
|
||||
|
||||
const handleDownload = async (code:string) => {
|
||||
await setDownloadCode(code)
|
||||
const {data:result} = await refetchShareCodeWithResource()
|
||||
console.log('下载', result);
|
||||
handleValidSuccess(result.resource.url, result.fileName);
|
||||
saveAndUpdateRecord(result as any as ShareCodeResponse, 'shareCodeDownloadRecords');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<HistoryOutlined />}
|
||||
onClick={handleOpen}
|
||||
style={{ marginBottom: '16px' }}
|
||||
>
|
||||
{btnContent}
|
||||
</Button>
|
||||
<Drawer
|
||||
title={title}
|
||||
placement="right"
|
||||
width={420}
|
||||
onClose={handleClose}
|
||||
open={open}
|
||||
>
|
||||
{records.length > 0 ? (
|
||||
<div className="space-y-4">
|
||||
{records.map(item => (
|
||||
<ShareCodeListCard
|
||||
key={item.code}
|
||||
item={item}
|
||||
onDelete={handleDelete}
|
||||
onDownload={isDownload ? handleDownload : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<Empty description="暂无分享码记录" />
|
||||
)}
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -7,6 +7,7 @@ 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 {
|
||||
|
@ -14,11 +15,7 @@ interface ShareCodeGeneratorProps {
|
|||
onSuccess?: (code: string) => void;
|
||||
fileName?: string;
|
||||
}
|
||||
interface ShareCodeResponse {
|
||||
code?: string;
|
||||
expiresAt?: Date;
|
||||
canUseTimes?: number;
|
||||
}
|
||||
|
||||
|
||||
export function copyToClipboard(text) {
|
||||
if (navigator.clipboard) {
|
||||
|
@ -35,7 +32,6 @@ export function copyToClipboard(text) {
|
|||
}
|
||||
export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
|
||||
fileId,
|
||||
onSuccess,
|
||||
fileName,
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
@ -47,6 +43,7 @@ export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
|
|||
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 });
|
||||
|
@ -74,10 +71,10 @@ export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
|
|||
console.log('data', data)
|
||||
setShareCode(data.code);
|
||||
setIsGenerate(true);
|
||||
onSuccess?.(data.code);
|
||||
setExpiresAt(dayjs(data.expiresAt).format('YYYY-MM-DD HH:mm:ss'));
|
||||
setCanUseTimes(data.canUseTimes);
|
||||
//message.success('分享码生成成功');
|
||||
saveCodeRecord(data,'shareCodeGeneratorRecords');
|
||||
message.success('分享码生成成功'+data.code);
|
||||
} catch (error) {
|
||||
console.error('生成分享码错误:', error);
|
||||
message.error('生成分享码失败: ' + (error instanceof Error ? error.message : '未知错误'));
|
||||
|
|
|
@ -1,50 +1,42 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Input, Button, message } from 'antd';
|
||||
import { Input, Button, message, Spin } from 'antd';
|
||||
import styles from './ShareCodeValidator.module.css';
|
||||
import { api } from '@nice/client';
|
||||
import dayjs from 'dayjs';
|
||||
interface ShareCodeValidatorProps {
|
||||
onValidSuccess: (fileId: string, fileName: string) => void;
|
||||
}
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import { env } from '@web/src/env';
|
||||
import { copyToClipboard } from './sharecodegenerator';
|
||||
import { ShareCodeResponse, useQuickFileContext } from '../quick-file/quickFileContext';
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
export const ShareCodeValidator: React.FC<ShareCodeValidatorProps> = ({
|
||||
onValidSuccess,
|
||||
}) => {
|
||||
const [code, setCode] = useState('');
|
||||
const { data: result, isLoading, refetch } = api.shareCode.getFileByShareCode.useQuery(
|
||||
{ code: code.trim() },
|
||||
{
|
||||
enabled: false
|
||||
}
|
||||
)
|
||||
export const ShareCodeValidator: React.FC = ({ }) => {
|
||||
const { saveCodeRecord,handleValidSuccess,isGetingFileId,downloadCode,setDownloadCode,refetchShareCodeWithResource,isLoading,downloadResult } = useQuickFileContext();
|
||||
|
||||
const validateCode = useCallback(async () => {
|
||||
|
||||
if (!code.trim()) {
|
||||
if (!downloadCode?.trim()) {
|
||||
message.warning('请输入正确的分享码');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { data: latestResult } = await refetch();
|
||||
const { data: latestResult } = await refetchShareCodeWithResource();
|
||||
console.log('验证分享码返回数据:', latestResult);
|
||||
onValidSuccess(latestResult.url, latestResult.fileName);
|
||||
saveCodeRecord(latestResult as any as ShareCodeResponse,'shareCodeDownloadRecords')
|
||||
handleValidSuccess(latestResult.resource.url, latestResult.fileName);
|
||||
} catch (error) {
|
||||
console.error('验证分享码失败:', error);
|
||||
message.error('分享码无效或已过期');
|
||||
}
|
||||
}, [refetch, code, onValidSuccess]);
|
||||
}, [refetchShareCodeWithResource, downloadCode, handleValidSuccess]);
|
||||
const getDownloadUrl = useCallback(async () => {
|
||||
try {
|
||||
const { data: latestResult } = await refetch();
|
||||
const { data: latestResult } = await refetchShareCodeWithResource();
|
||||
console.log('验证分享码返回数据:', latestResult);
|
||||
const downloadUrl = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${latestResult.url}`;
|
||||
const downloadUrl = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${latestResult.resource.url}`;
|
||||
copyToClipboard(downloadUrl)
|
||||
.then(() => {
|
||||
message.success(`${latestResult.fileName}的下载链接已复制`,6)
|
||||
message.success(`${latestResult.fileName}的下载链接已复制`, 6)
|
||||
})
|
||||
.catch(() => {
|
||||
message.error('复制失败')
|
||||
|
@ -53,14 +45,19 @@ export const ShareCodeValidator: React.FC<ShareCodeValidatorProps> = ({
|
|||
console.error('验证分享码失败:', error);
|
||||
message.error('分享码无效或已过期');
|
||||
}
|
||||
}, [refetch, code, onValidSuccess]);
|
||||
}, [refetchShareCodeWithResource, downloadCode, handleValidSuccess]);
|
||||
return (
|
||||
<>
|
||||
{
|
||||
isGetingFileId ?
|
||||
(<Spin spinning={isGetingFileId} fullscreen />) :
|
||||
(
|
||||
<>
|
||||
<div className={styles.container}>
|
||||
<Input
|
||||
className={styles.input}
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value.toUpperCase())}
|
||||
value={downloadCode}
|
||||
onChange={(e) => setDownloadCode(e.target.value.toUpperCase())}
|
||||
placeholder="请输入分享码"
|
||||
maxLength={8}
|
||||
onPressEnter={validateCode}
|
||||
|
@ -69,7 +66,7 @@ export const ShareCodeValidator: React.FC<ShareCodeValidatorProps> = ({
|
|||
type="primary"
|
||||
onClick={getDownloadUrl}
|
||||
loading={isLoading}
|
||||
disabled={!code.trim()}
|
||||
disabled={!downloadCode?.trim()}
|
||||
>
|
||||
获取下载链接
|
||||
</Button>
|
||||
|
@ -77,21 +74,24 @@ export const ShareCodeValidator: React.FC<ShareCodeValidatorProps> = ({
|
|||
type="primary"
|
||||
onClick={validateCode}
|
||||
loading={isLoading}
|
||||
disabled={!code.trim()}
|
||||
disabled={!downloadCode?.trim()}
|
||||
>
|
||||
下载
|
||||
</Button>
|
||||
</div>
|
||||
{
|
||||
!isLoading && result && (
|
||||
!isLoading && downloadResult && (
|
||||
<div className='w-full flex justify-between my-2 p-1 antialiased text-secondary-600'>
|
||||
<span >{`分享码:${result?.code ? result.code : ''}`}</span>
|
||||
<span >{`文件名:${result?.fileName ? result.fileName : ''}`}</span>
|
||||
<span >{`过期时间:${result?.expiresAt ? dayjs(result.expiresAt).format('YYYY-MM-DD HH:mm:ss') : ''}`}</span>
|
||||
<span >{`剩余使用次数:${result?.canUseTimes ? result.canUseTimes : ''}`}</span>
|
||||
<span >{`分享码:${downloadResult?.code ? downloadResult.code : ''}`}</span>
|
||||
<span >{`文件名:${downloadResult?.fileName ? downloadResult.fileName : ''}`}</span>
|
||||
<span >{`过期时间:${downloadResult?.expiresAt ? dayjs(downloadResult.expiresAt).format('YYYY-MM-DD HH:mm:ss') : ''}`}</span>
|
||||
<span >{`剩余使用次数:${downloadResult?.canUseTimes ? downloadResult.canUseTimes : ''}`}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -11,6 +11,7 @@ import WithAuth from "../components/utils/with-auth";
|
|||
import { CodeManageProvider } from "../app/admin/code-manage/CodeManageContext";
|
||||
import CodeManageLayout from "../app/admin/code-manage/CodeManageLayout";
|
||||
import QuickUploadPage from "../app/admin/quick-file/page";
|
||||
import { QuickFileProvider } from "../app/admin/quick-file/quickFileContext";
|
||||
interface CustomIndexRouteObject extends IndexRouteObject {
|
||||
name?: string;
|
||||
breadcrumb?: string;
|
||||
|
@ -34,7 +35,11 @@ export type CustomRouteObject =
|
|||
export const routes: CustomRouteObject[] = [
|
||||
{
|
||||
path: "/",
|
||||
element: <QuickUploadPage></QuickUploadPage>,
|
||||
element: <>
|
||||
<QuickFileProvider>
|
||||
<QuickUploadPage></QuickUploadPage>
|
||||
</QuickFileProvider>
|
||||
</>,
|
||||
errorElement: <ErrorPage />,
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue