rht
This commit is contained in:
parent
9f1bc38acd
commit
c6e92cb876
|
@ -20,6 +20,7 @@ export interface ShareCode {
|
||||||
isUsed: boolean;
|
isUsed: boolean;
|
||||||
fileName?: string | null;
|
fileName?: string | null;
|
||||||
canUseTimes: number | null;
|
canUseTimes: number | null;
|
||||||
|
uploadIp?: string;
|
||||||
}
|
}
|
||||||
export interface GenerateShareCodeResponse {
|
export interface GenerateShareCodeResponse {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -154,7 +155,9 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
||||||
filetype,
|
filetype,
|
||||||
size,
|
size,
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
uploadIp,
|
||||||
|
createdAt: new Date()
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to generate share code', error);
|
this.logger.error('Failed to generate share code', error);
|
||||||
|
@ -172,6 +175,7 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
||||||
code,
|
code,
|
||||||
isUsed: false,
|
isUsed: false,
|
||||||
expiresAt: { gt: dayjs().tz('Asia/Shanghai').toDate() },
|
expiresAt: { gt: dayjs().tz('Asia/Shanghai').toDate() },
|
||||||
|
deletedAt: null
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (shareCode.canUseTimes <= 0) {
|
if (shareCode.canUseTimes <= 0) {
|
||||||
|
@ -280,7 +284,8 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
||||||
this.logger.log('验证分享码结果:', shareCode);
|
this.logger.log('验证分享码结果:', shareCode);
|
||||||
|
|
||||||
if (!shareCode) {
|
if (!shareCode) {
|
||||||
throw new NotFoundException('分享码无效或已过期');
|
this.logger.log('分享码无效或已过期');
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取文件信息
|
// 获取文件信息
|
||||||
|
@ -308,7 +313,9 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
|
||||||
meta: {
|
meta: {
|
||||||
filename, filetype, size
|
filename, filetype, size
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
uploadIp: shareCode.uploadIp,
|
||||||
|
createdAt: shareCode.createdAt
|
||||||
};
|
};
|
||||||
|
|
||||||
this.logger.log('返回给前端的数据:', response); // 添加日志
|
this.logger.log('返回给前端的数据:', response); // 添加日志
|
||||||
|
|
|
@ -45,7 +45,8 @@ export const CodeManageProvider = ({ children }: { children: React.ReactNode })
|
||||||
...(searchKeyword ? {
|
...(searchKeyword ? {
|
||||||
OR: [
|
OR: [
|
||||||
{ fileName: { contains: searchKeyword } },
|
{ fileName: { contains: searchKeyword } },
|
||||||
{ code: { contains: searchKeyword } }
|
{ code: { contains: searchKeyword } },
|
||||||
|
{ uploadIp: { contains: searchKeyword } }
|
||||||
]
|
]
|
||||||
} : {})
|
} : {})
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { Button, Form, Input } from 'antd';
|
import { Button, Form, Input } from 'antd';
|
||||||
import { useCodeManageContext } from '../CodeManageContext';
|
import { useCodeManageContext } from '../CodeManageContext';
|
||||||
import { ChangeEvent, useEffect, useRef } from 'react';
|
import { ChangeEvent, useEffect, useRef } from 'react';
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
export default function CodeManageSearchBase({ keyword }: { keyword?: string }) {
|
export default function CodeManageSearchBase({ keyword }: { keyword?: string }) {
|
||||||
const { setCurrentPage, searchRefetch, setSearchKeyword, searchKeyword } = useCodeManageContext();
|
const { setCurrentPage, searchRefetch, setSearchKeyword, searchKeyword } = useCodeManageContext();
|
||||||
const debounceTimer = useRef<NodeJS.Timeout | null>(null);
|
const debounceTimer = useRef<NodeJS.Timeout | null>(null);
|
||||||
const formRef = Form.useForm()[0]; // 获取表单实例
|
const formRef = Form.useForm()[0]; // 获取表单实例
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
// 当 keyword 属性变化时更新表单值
|
// 当 keyword 属性变化时更新表单值
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -14,6 +16,17 @@ export default function CodeManageSearchBase({ keyword }: { keyword?: string })
|
||||||
}
|
}
|
||||||
}, [keyword, formRef]);
|
}, [keyword, formRef]);
|
||||||
|
|
||||||
|
// 监听 searchKeyword 变化,如果 URL 参数中有 keyword 且 searchKeyword 发生变化,则清空 URL 参数
|
||||||
|
useEffect(() => {
|
||||||
|
const urlKeyword = searchParams.get('keyword');
|
||||||
|
if (urlKeyword && searchKeyword !== urlKeyword) {
|
||||||
|
// 创建一个新的 URLSearchParams 对象,不包含 keyword 参数
|
||||||
|
const newParams = new URLSearchParams(searchParams);
|
||||||
|
newParams.delete('keyword');
|
||||||
|
setSearchParams(newParams);
|
||||||
|
}
|
||||||
|
}, [searchKeyword, searchParams, setSearchParams]);
|
||||||
|
|
||||||
const onSearch = (value: string) => {
|
const onSearch = (value: string) => {
|
||||||
console.log(value);
|
console.log(value);
|
||||||
setSearchKeyword(value);
|
setSearchKeyword(value);
|
||||||
|
@ -41,9 +54,10 @@ export default function CodeManageSearchBase({ keyword }: { keyword?: string })
|
||||||
<Form form={formRef}>
|
<Form form={formRef}>
|
||||||
<Form.Item name="search" label="关键字搜索">
|
<Form.Item name="search" label="关键字搜索">
|
||||||
<Input.Search
|
<Input.Search
|
||||||
placeholder="输入分享码或文件名"
|
placeholder="输入分享码、文件名或用户IP"
|
||||||
enterButton
|
enterButton
|
||||||
value={searchKeyword}
|
value={searchKeyword}
|
||||||
|
allowClear
|
||||||
onSearch={onSearch}
|
onSearch={onSearch}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -25,8 +25,11 @@ export default function Activate() {
|
||||||
<ActivityItem
|
<ActivityItem
|
||||||
key={item.id}
|
key={item.id}
|
||||||
icon={<FileOutlined className="text-blue-500" />}
|
icon={<FileOutlined className="text-blue-500" />}
|
||||||
title={`来自${item.uploadIp}的用户上传了文件:"${item.fileName}",文件大小:${Math.max(Number(item.resource.meta.size) / 1024 / 1024, 0.01).toFixed(2)}MB,分享码:${item.code}`}
|
|
||||||
time={item.createdAt?.toLocaleString()}
|
time={item.createdAt?.toLocaleString()}
|
||||||
|
ip={item.uploadIp}
|
||||||
|
filename={item.fileName}
|
||||||
|
filesize={Math.max(Number(item.resource.meta.size) / 1024 / 1024, 0.01).toFixed(2)}
|
||||||
|
code={item.code}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -2,11 +2,14 @@ import { motion } from 'framer-motion';
|
||||||
// 活动项组件
|
// 活动项组件
|
||||||
interface ActivityItemProps {
|
interface ActivityItemProps {
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
title: string;
|
|
||||||
time: string;
|
time: string;
|
||||||
|
ip: string;
|
||||||
|
filename: string;
|
||||||
|
filesize: string;
|
||||||
|
code: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ActivityItem({ icon, title, time }: ActivityItemProps) {
|
export default function ActivityItem({ icon, time, ip ,filename, filesize, code }: ActivityItemProps) {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
className="flex items-center py-2"
|
className="flex items-center py-2"
|
||||||
|
@ -18,7 +21,12 @@ export default function ActivityItem({ icon, title, time }: ActivityItemProps) {
|
||||||
{icon}
|
{icon}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow">
|
<div className="flex-grow">
|
||||||
<div className="text-gray-800">{title}</div>
|
<div className='text-gray-500'>
|
||||||
|
来自<span className='text-red-500'>{ip}</span>的用户
|
||||||
|
上传了文件:<span className='text-blue-500'>"{filename}"</span>,
|
||||||
|
文件大小:<span className='text-gray-500'>{filesize}MB</span>,
|
||||||
|
分享码:<span className='text-primary-900'>{code}</span>
|
||||||
|
</div>
|
||||||
<div className="text-gray-400 text-xs">{time}</div>
|
<div className="text-gray-400 text-xs">{time}</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
|
@ -74,7 +74,7 @@ export default function Board() {
|
||||||
className="h-full"
|
className="h-full"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col h-full justify-between py-2">
|
<div className="flex flex-col h-full justify-between py-2">
|
||||||
<Statistic value={isShareCodeAllLoading ? 0 : `${(shareCodeAll?.totalSize / 1024 / 1024).toFixed(2)}MB`} />
|
<Statistic value={isShareCodeAllLoading ? 0 : `${(shareCodeAll?.totalSize / 1024 / 1024 / 1024).toFixed(2)}GB`} />
|
||||||
{
|
{
|
||||||
isShareCodeTodayLoading || isShareCodeYesterdayLoading
|
isShareCodeTodayLoading || isShareCodeYesterdayLoading
|
||||||
? <Spin />
|
? <Spin />
|
||||||
|
|
|
@ -13,7 +13,7 @@ export const Header: React.FC<HeaderProps> = ({ showLoginButton = true }) => {
|
||||||
const isAdmin = hasEveryPermissions(RolePerms.MANAGE_ANY_POST) && isAuthenticated;
|
const isAdmin = hasEveryPermissions(RolePerms.MANAGE_ANY_POST) && isAuthenticated;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-[1000px] mx-auto flex justify-between items-center mt-2">
|
<div className="w-[1000px] mx-auto flex justify-between items-center pt-3">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<img src="/logo.svg" className="h-16 w-16" />
|
<img src="/logo.svg" className="h-16 w-16" />
|
||||||
<span className="text-4xl py-4 mx-4 font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-blue-700 drop-shadow-sm">烽火快传</span>
|
<span className="text-4xl py-4 mx-4 font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-blue-700 drop-shadow-sm">烽火快传</span>
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { Layout, Menu } from "antd";
|
import { Layout, Menu, Button, Space } from "antd";
|
||||||
import { FileOutlined, DashboardOutlined } from "@ant-design/icons";
|
import { FileOutlined, DashboardOutlined, HomeOutlined, LogoutOutlined } from "@ant-design/icons";
|
||||||
import { NavLink, Outlet, useNavigate, useLocation } from "react-router-dom";
|
import { NavLink, Outlet, useNavigate, useLocation } from "react-router-dom";
|
||||||
|
import { useAuth } from "@web/src/providers/auth-provider";
|
||||||
|
|
||||||
const { Sider, Content } = Layout;
|
const { Sider, Content } = Layout;
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ export default function QuickFileManage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [currentKey, setCurrentKey] = useState("1");
|
const [currentKey, setCurrentKey] = useState("1");
|
||||||
|
const { logout } = useAuth();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 根据当前URL路径设置currentKey
|
// 根据当前URL路径设置currentKey
|
||||||
|
@ -28,15 +30,12 @@ export default function QuickFileManage() {
|
||||||
onCollapse={(value) => setCollapsed(value)}
|
onCollapse={(value) => setCollapsed(value)}
|
||||||
theme="light"
|
theme="light"
|
||||||
style={{
|
style={{
|
||||||
borderRight: "1px solid #f0f0f0"
|
borderRight: "1px solid #f0f0f0",
|
||||||
}}
|
display: "flex",
|
||||||
>
|
flexDirection: "column"
|
||||||
<div
|
|
||||||
className="flex items-center justify-center cursor-pointer w-full"
|
|
||||||
onClick={() => {
|
|
||||||
navigate('/');
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<div className="flex items-center justify-center cursor-pointer w-full">
|
||||||
<img src="/logo.svg" className="h-10 w-10" />
|
<img src="/logo.svg" className="h-10 w-10" />
|
||||||
{!collapsed && <span className="text-2xl py-4 mx-4 font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-blue-700 drop-shadow-sm">烽火快传</span>}
|
{!collapsed && <span className="text-2xl py-4 mx-4 font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-blue-700 drop-shadow-sm">烽火快传</span>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,6 +56,27 @@ export default function QuickFileManage() {
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
<div className="mt-auto p-2 flex justify-between" style={{marginTop: "auto"}}>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
icon={<HomeOutlined />}
|
||||||
|
onClick={() => navigate('/')}
|
||||||
|
style={{flex: 1}}
|
||||||
|
>
|
||||||
|
{!collapsed && "首页"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
icon={<LogoutOutlined />}
|
||||||
|
onClick={async () => {
|
||||||
|
navigate('/');
|
||||||
|
await logout();
|
||||||
|
}}
|
||||||
|
style={{flex: 1}}
|
||||||
|
>
|
||||||
|
{!collapsed && "登出"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</Sider>
|
</Sider>
|
||||||
<Content style={{ padding: "24px" }}>
|
<Content style={{ padding: "24px" }}>
|
||||||
<Outlet></Outlet>
|
<Outlet></Outlet>
|
||||||
|
|
|
@ -10,49 +10,39 @@ export default function QuickUploadPage() {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const uploadFileId = Form.useWatch(["file"], form)?.[0]
|
const uploadFileId = Form.useWatch(["file"], form)?.[0]
|
||||||
return (
|
return (
|
||||||
<div className="w-full ">
|
<div className="w-full min-h-screen bg-gray-50">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="max-w-[1000px] mx-auto">
|
<div className="max-w-[1000px] mx-auto px-4 py-6">
|
||||||
<div className="my-4 p-5 border-3 border-gray-200 rounded-lg shadow-lg">
|
<div className="my-1 p-6 bg-white border border-gray-100 rounded-xl shadow-md hover:shadow-lg transition-shadow duration-300">
|
||||||
<div className="flex justify-between items-center mb-4">
|
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-5 pb-4 border-b border-gray-100">
|
||||||
<span className="text-lg block text-zinc-700 py-4">使用分享码下载文件</span>
|
<span className="text-xl font-medium text-gray-800">使用分享码下载文件</span>
|
||||||
<CodeRecord title="我使用的分享码" btnContent="使用记录" recordName="shareCodeDownloadRecords" isDownload={true} />
|
<CodeRecord title="我使用的分享码" btnContent="使用记录" recordName="shareCodeDownloadRecords" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="bg-gray-50 bg-opacity-70 rounded-lg p-4">
|
||||||
<ShareCodeValidator />
|
<ShareCodeValidator />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="my-4 p-5 border-3 border-gray-200 rounded-lg shadow-lg">
|
|
||||||
<div className="flex justify-between items-center mb-4">
|
<div className="my-6 p-6 bg-white border border-gray-100 rounded-xl shadow-md hover:shadow-lg transition-shadow duration-300">
|
||||||
<span className="text-lg text-zinc-700">上传文件并生成分享码</span>
|
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-5 pb-4 border-b border-gray-100">
|
||||||
|
<span className="text-xl font-medium text-gray-800">上传文件并生成分享码</span>
|
||||||
<CodeRecord title="我生成的分享码" btnContent="上传记录" recordName="shareCodeGeneratorRecords" />
|
<CodeRecord title="我生成的分享码" btnContent="上传记录" recordName="shareCodeGeneratorRecords" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="mb-8">
|
||||||
|
<ShareCodeGenerator fileId={uploadFileId} />
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
<Form form={form}>
|
<Form form={form}>
|
||||||
<Form.Item name="file">
|
<Form.Item name="file">
|
||||||
<TusUploader
|
<TusUploader
|
||||||
multiple={false}
|
multiple={false}
|
||||||
style={"w-full py-4 mb-0 h-64"}
|
style="w-full py-8 px-4 mb-0 h-72 border-2 border-dashed border-gray-200 hover:border-blue-400 bg-gray-50 bg-opacity-50 rounded-lg transition-colors"
|
||||||
></TusUploader>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ marginBottom: '40px' }}>
|
|
||||||
<ShareCodeGenerator
|
|
||||||
fileId={uploadFileId}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <Tabs defaultActiveKey="upload">
|
|
||||||
<TabPane tab="上传分享" key="upload">
|
|
||||||
|
|
||||||
</TabPane>
|
|
||||||
<TabPane tab="下载文件" key="download">
|
|
||||||
|
|
||||||
</TabPane>
|
|
||||||
</Tabs> */}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,15 @@ export const QuickFileProvider = ({ children }: { children: React.ReactNode }) =
|
||||||
const [isGetingFileId, setIsGetingFileId] = useState(false);
|
const [isGetingFileId, setIsGetingFileId] = useState(false);
|
||||||
const [downloadCode, setDownloadCode] = useState<string | null>(null);
|
const [downloadCode, setDownloadCode] = useState<string | null>(null);
|
||||||
const saveCodeRecord = (data: ShareCodeResponse, recordName: string) => {
|
const saveCodeRecord = (data: ShareCodeResponse, recordName: string) => {
|
||||||
|
if (data.canUseTimes == 0) {
|
||||||
|
const existingGeneratorRecords = localStorage.getItem(recordName);
|
||||||
|
if (existingGeneratorRecords && data.code) {
|
||||||
|
const generatorRecords = JSON.parse(existingGeneratorRecords);
|
||||||
|
const filteredRecords = generatorRecords.filter((item: ShareCodeResponse) => item.code !== data.code);
|
||||||
|
localStorage.setItem(recordName, JSON.stringify(filteredRecords));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
const newRecord = {
|
const newRecord = {
|
||||||
id: `${Date.now()}`, // 生成唯一ID
|
id: `${Date.now()}`, // 生成唯一ID
|
||||||
code: data.code,
|
code: data.code,
|
||||||
|
@ -55,7 +64,9 @@ export const QuickFileProvider = ({ children }: { children: React.ReactNode }) =
|
||||||
filename: data.resource.meta.filename,
|
filename: data.resource.meta.filename,
|
||||||
filetype: data.resource.meta.filetype
|
filetype: data.resource.meta.filetype
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
uploadIp: data.uploadIp,
|
||||||
|
createdAt: data.createdAt
|
||||||
};
|
};
|
||||||
// 获取已有记录并添加新记录
|
// 获取已有记录并添加新记录
|
||||||
const existingGeneratorRecords = localStorage.getItem(recordName);
|
const existingGeneratorRecords = localStorage.getItem(recordName);
|
||||||
|
|
|
@ -43,11 +43,12 @@ export default function CodeRecord({ title, btnContent, recordName ,styles,isDow
|
||||||
localStorage.setItem(recordName, JSON.stringify(updatedRecords));
|
localStorage.setItem(recordName, JSON.stringify(updatedRecords));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownload = async (code:string) => {
|
const handleDownload = async (code:string,recordName:string) => {
|
||||||
await setDownloadCode(code)
|
await setDownloadCode(code)
|
||||||
const {data:result} = await refetchShareCodeWithResource()
|
const {data:result} = await refetchShareCodeWithResource()
|
||||||
console.log('下载', result);
|
console.log('下载', result);
|
||||||
handleValidSuccess(result.resource.url, result.fileName);
|
handleValidSuccess(result.resource.url, result.fileName);
|
||||||
|
saveAndUpdateRecord(result as any as ShareCodeResponse, 'shareCodeUploadRecords');
|
||||||
saveAndUpdateRecord(result as any as ShareCodeResponse, 'shareCodeDownloadRecords');
|
saveAndUpdateRecord(result as any as ShareCodeResponse, 'shareCodeDownloadRecords');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ export default function CodeRecord({ title, btnContent, recordName ,styles,isDow
|
||||||
key={item.id}
|
key={item.id}
|
||||||
item={item}
|
item={item}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
onDownload={isDownload ? handleDownload : undefined}
|
onDownload={() => handleDownload(item.code, recordName)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Button, DatePicker, Form, message, Select } from 'antd';
|
import { Button, DatePicker, Form, InputNumber, message } from 'antd';
|
||||||
import { CopyOutlined } from '@ant-design/icons';
|
import { CopyOutlined } from '@ant-design/icons';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { getQueryKey } from '@trpc/react-query';
|
import { getQueryKey } from '@trpc/react-query';
|
||||||
|
@ -73,8 +73,8 @@ export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
|
||||||
setIsGenerate(true);
|
setIsGenerate(true);
|
||||||
setExpiresAt(dayjs(data.expiresAt).format('YYYY-MM-DD HH:mm:ss'));
|
setExpiresAt(dayjs(data.expiresAt).format('YYYY-MM-DD HH:mm:ss'));
|
||||||
setCanUseTimes(data.canUseTimes);
|
setCanUseTimes(data.canUseTimes);
|
||||||
saveCodeRecord(data,'shareCodeGeneratorRecords');
|
saveCodeRecord(data, 'shareCodeGeneratorRecords');
|
||||||
message.success('分享码生成成功'+data.code);
|
message.success('分享码生成成功' + data.code);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('生成分享码错误:', error);
|
console.error('生成分享码错误:', error);
|
||||||
message.error('生成分享码失败: ' + (error instanceof Error ? error.message : '未知错误'));
|
message.error('生成分享码失败: ' + (error instanceof Error ? error.message : '未知错误'));
|
||||||
|
@ -92,7 +92,7 @@ export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
|
||||||
const date = dayjs().add(1, 'day').tz('Asia/Shanghai');
|
const date = dayjs().add(1, 'day').tz('Asia/Shanghai');
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
expiresAt: date,
|
expiresAt: date,
|
||||||
canUseTimes: 10
|
canUseTimes: 5
|
||||||
});
|
});
|
||||||
}, [form]);
|
}, [form]);
|
||||||
|
|
||||||
|
@ -116,7 +116,10 @@ export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
|
||||||
<Form.Item name="expiresAt" className='mt-5'>
|
<Form.Item name="expiresAt" className='mt-5'>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
showTime
|
showTime
|
||||||
disabledDate={(current) => current && current < dayjs().startOf('day')}
|
disabledDate={(current) =>
|
||||||
|
(current && current < dayjs().startOf('day')) ||
|
||||||
|
(current && current > dayjs().add(7, 'day').endOf('day'))
|
||||||
|
}
|
||||||
disabledTime={(current) => {
|
disabledTime={(current) => {
|
||||||
if (current && current.isSame(dayjs(), 'day')) {
|
if (current && current.isSame(dayjs(), 'day')) {
|
||||||
return {
|
return {
|
||||||
|
@ -137,16 +140,11 @@ export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
|
||||||
{"分享码的使用次数"}
|
{"分享码的使用次数"}
|
||||||
</small>
|
</small>
|
||||||
<Form.Item name="canUseTimes" className='mt-5'>
|
<Form.Item name="canUseTimes" className='mt-5'>
|
||||||
<Select
|
<InputNumber
|
||||||
style={{ width: 120 }}
|
style={{ width: 120 }}
|
||||||
//onChange={handleChange}
|
min={1}
|
||||||
options={[
|
max={10}
|
||||||
{ value: 10, label: '10' },
|
defaultValue={5}
|
||||||
{ value: 20, label: '20' },
|
|
||||||
{ value: 30, label: '30' },
|
|
||||||
{ value: 40, label: '40' },
|
|
||||||
{ value: 50, label: '50' },
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,8 +22,12 @@ export const ShareCodeValidator: React.FC = ({ }) => {
|
||||||
try {
|
try {
|
||||||
const { data: latestResult } = await refetchShareCodeWithResource();
|
const { data: latestResult } = await refetchShareCodeWithResource();
|
||||||
console.log('验证分享码返回数据:', latestResult);
|
console.log('验证分享码返回数据:', latestResult);
|
||||||
|
if (latestResult) {
|
||||||
saveCodeRecord(latestResult as any as ShareCodeResponse,'shareCodeDownloadRecords')
|
saveCodeRecord(latestResult as any as ShareCodeResponse,'shareCodeDownloadRecords')
|
||||||
handleValidSuccess(latestResult.resource.url, latestResult.fileName);
|
handleValidSuccess(latestResult.resource.url, latestResult.fileName);
|
||||||
|
} else {
|
||||||
|
message.error('分享码无效或已过期');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('验证分享码失败:', error);
|
console.error('验证分享码失败:', error);
|
||||||
message.error('分享码无效或已过期');
|
message.error('分享码无效或已过期');
|
||||||
|
@ -32,6 +36,7 @@ export const ShareCodeValidator: React.FC = ({ }) => {
|
||||||
const getDownloadUrl = useCallback(async () => {
|
const getDownloadUrl = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const { data: latestResult } = await refetchShareCodeWithResource();
|
const { data: latestResult } = await refetchShareCodeWithResource();
|
||||||
|
saveCodeRecord(latestResult as any as ShareCodeResponse,'shareCodeDownloadRecords')
|
||||||
console.log('验证分享码返回数据:', latestResult);
|
console.log('验证分享码返回数据:', latestResult);
|
||||||
const downloadUrl = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${latestResult.resource.url}`;
|
const downloadUrl = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${latestResult.resource.url}`;
|
||||||
copyToClipboard(downloadUrl)
|
copyToClipboard(downloadUrl)
|
||||||
|
@ -85,7 +90,7 @@ export const ShareCodeValidator: React.FC = ({ }) => {
|
||||||
<span >{`分享码:${downloadResult?.code ? downloadResult.code : ''}`}</span>
|
<span >{`分享码:${downloadResult?.code ? downloadResult.code : ''}`}</span>
|
||||||
<span >{`文件名:${downloadResult?.fileName ? downloadResult.fileName : ''}`}</span>
|
<span >{`文件名:${downloadResult?.fileName ? downloadResult.fileName : ''}`}</span>
|
||||||
<span >{`过期时间:${downloadResult?.expiresAt ? dayjs(downloadResult.expiresAt).format('YYYY-MM-DD HH:mm:ss') : ''}`}</span>
|
<span >{`过期时间:${downloadResult?.expiresAt ? dayjs(downloadResult.expiresAt).format('YYYY-MM-DD HH:mm:ss') : ''}`}</span>
|
||||||
<span >{`剩余使用次数:${downloadResult?.canUseTimes ? downloadResult.canUseTimes : ''}`}</span>
|
<span >{`剩余使用次数:${downloadResult?.canUseTimes ? downloadResult.canUseTimes : 0}`}</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ server {
|
||||||
# 仅供内部使用
|
# 仅供内部使用
|
||||||
internal;
|
internal;
|
||||||
# 代理到认证服务
|
# 代理到认证服务
|
||||||
proxy_pass http://192.168.43.206:3001/auth/file;
|
proxy_pass http://192.168.43.206:3006/auth/file;
|
||||||
|
|
||||||
# 请求优化:不传递请求体
|
# 请求优化:不传递请求体
|
||||||
proxy_pass_request_body off;
|
proxy_pass_request_body off;
|
||||||
|
|
BIN
web-dist.zip
BIN
web-dist.zip
Binary file not shown.
Loading…
Reference in New Issue