feat: 文件下载

This commit is contained in:
2026-02-28 17:26:13 +08:00
parent 62abb284a6
commit 4418a95822
9 changed files with 155 additions and 31 deletions

View File

@@ -17,7 +17,7 @@ export default {
// http://192.168.1.231:48080 伟强
// http://192.168.1.89:48086 子杰
// https://petshy.tashowz.com/
target: 'http://192.168.1.89:48086',
target: 'http://192.168.1.89:48080',
changeOrigin: true,
},
},

View File

@@ -32,6 +32,7 @@ const GroupTagCore: React.FC<GroupTagCoreProps> = (props) => {
const [type, setType] = useState<'group' | 'tag'>('group');
const [visible, setVisible] = useState<boolean>(false);
const [name, setName] = useState<string>('');
const [tagName, setTagName] = useState<string>('');
const [total, setTotal] = useState<number>(0);
const [currentId, setCurrentId] = useState<number>();
const [tagsModalValue, setTagsModalValue] = useState<{
@@ -56,7 +57,11 @@ const GroupTagCore: React.FC<GroupTagCoreProps> = (props) => {
const fetchTagsApi = useCallback(async () => {
try {
setLoadingTags(true);
const res = await tagsApi.get({ groupId: currentId, pageNo: 1 });
const res = await tagsApi.get({
groupId: currentId,
pageNo: 1,
tagName: tagName,
});
const newGroup = { ...currentGroup, tags: res.list };
const newData = groups.map((g) => (g.id === currentId ? newGroup : g));
setGroups(newData as GroupItem[]);
@@ -238,6 +243,7 @@ const GroupTagCore: React.FC<GroupTagCoreProps> = (props) => {
const onSearchTags = async (e: React.KeyboardEvent<HTMLInputElement>) => {
const searchValue = (e.target as HTMLInputElement).value;
setTagName(searchValue);
const res = await tagsApi.get({
groupId: currentId,
pageNo: 1,
@@ -247,11 +253,15 @@ const GroupTagCore: React.FC<GroupTagCoreProps> = (props) => {
...currentGroup,
tags: res.list,
};
setTotal(res.total);
document.getElementById('scrollableDiv')?.scrollTo(0, 0);
setCurrentGroup(newGroup as GroupItem);
setPageNo(1);
};
const onSearchTagsClear = async () => {
setTagName('');
fetchTagsApi();
};
return (
<div className={`group-tag-core`}>
<div className="search-wrapper">
@@ -260,6 +270,7 @@ const GroupTagCore: React.FC<GroupTagCoreProps> = (props) => {
placeholder="搜索"
onPressEnter={onSearchTags}
allowClear
onClear={onSearchTagsClear}
disabled={isInEditMode}
/>
</div>

View File

@@ -116,7 +116,7 @@ export const renderTags = (data: GroupTagProps) => {
<Empty description="暂无分组" image={Empty.PRESENTED_IMAGE_SIMPLE} />
);
}
console.log(list.length);
console.log(list, total);
return (
<InfiniteScroll
dataLength={list.length}

View File

@@ -32,7 +32,7 @@ const ModelPage = () => {
};
const onFetch = async (params: { pageSize?: number; current?: number }) => {
const data = await getModelList(params);
const data = await getModelList({ ...params, pageNo: params.current });
return {
data: data.list,
success: true,

View File

@@ -1,4 +1,5 @@
import type { ProColumns } from '@ant-design/pro-components';
import { Tag } from 'antd';
import GroupTagSelect from '@/components/GroupTag/GroupTagSelect';
import {
type AiSampleRespVO,
@@ -15,13 +16,32 @@ export const baseTenantColumns: ProColumns<AiSampleRespVO>[] = [
{
title: '样本名称',
dataIndex: 'sampleName',
// width: 500,
width: 200,
ellipsis: true,
},
{
title: '文件格式',
width: 100,
dataIndex: 'sampleMineType',
},
{
title: '标签',
dataIndex: 'tags',
width: 200,
hideInSearch: true,
render: (_, record) => {
return record.tags?.map((tag) => {
return <Tag key={tag.id}>{tag.tagName}</Tag>;
});
},
},
{
title: '注释',
width: 100,
dataIndex: 'remark',
hideInSearch: true,
},
{
title: '标签',
hideInTable: true,
@@ -57,11 +77,6 @@ export const baseTenantColumns: ProColumns<AiSampleRespVO>[] = [
);
},
},
{
title: '样本格式',
hideInTable: true,
dataIndex: 'sample_mine_type',
},
];
// export const formColumns = (data: {

View File

@@ -20,6 +20,8 @@ import {
deleteSampleTag,
deleteSampleTagGroup,
deleteSampleTagRelate,
downloadSample,
downloadZipFile,
getSampleTagGroup,
getSampleTagPage,
relateSample,
@@ -123,7 +125,22 @@ const SampleTagDetail = <T extends Record<string, any>>(
};
// 下载
const handleDownloadAll = () => {};
const handleDownloadAll = async () => {
const ids = data?.map((sample) => sample.id) as number[];
downloadZipFile(ids);
// const res = await downloadSample(ids);
// console.log(res);
// const downloadUrl = window.URL.createObjectURL(res);
// const link = document.createElement("a");
// link.href = downloadUrl;
// link.download = downloadUrl; // 设置下载的文件名
// link.style.display = "none";
// document.body.appendChild(link);
// link.click();
// document.body.removeChild(link);
// // const blob = new Blob([response.data], { type: 'application/pdf' });
// // const url = window.URL.createObjectURL(blob);
};
const handleTagManager = () => {
setTagManagerVisible(true);
@@ -158,6 +175,16 @@ const SampleTagDetail = <T extends Record<string, any>>(
setTagManagerVisible(false);
};
const onDownload = () => {
const item = data?.[0];
const link = document.createElement('a');
link.href = item?.sampleFilePath;
link.download = item?.sampleName; // 设置下载的文件名
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
return (
<>
<ProForm name="validate_other" formRef={formRef} submitter={false}>
@@ -312,7 +339,7 @@ const SampleTagDetail = <T extends Record<string, any>>(
onCancel={() => setTagManagerVisible(false)}
></TagManager>
<Space style={{ width: '100%', justifyContent: 'center', padding: 12 }}>
<Button color="danger" onClick={() => {}}>
<Button color="danger" onClick={onDownload}>
</Button>
<Button color="danger" variant="solid" onClick={handleDeleteAll}>

View File

@@ -58,6 +58,7 @@ const SampleTag: React.FC = () => {
const onFetch = async (params: SampleReqVo) => {
const data = await getSamplePage({
...params,
pageNo: params.current,
});
return {
data: data.list,
@@ -86,7 +87,7 @@ const SampleTag: React.FC = () => {
headerTitle="样本列表"
showIndex={false}
enableRowClick={true}
scroll={{ x: 'max-content' }}
scroll={{ x: 400 }}
rowSelection={{
type: selectTableType,
selectedRowKeys: selectedRows.map((item) => item.id) as React.Key[],

View File

@@ -134,23 +134,20 @@ export const errorConfig: RequestConfig = {
const { data } = response as unknown as ResponseStructure;
const config = response.config;
const { code } = data;
// if (!data) {
// // 返回“[HTTP]请求没有返回值”;
// throw new Error();
// }
// 未设置状态码则默认成功状态
// 二进制数据则直接返回,例如说 Excel 导出
// if (
// response.request.responseType === "blob" ||
// response.request.responseType === "arraybuffer"
// ) {
// // 注意:如果导出的响应为 json说明可能失败了不直接返回进行下载
// // if (response.data.type !== "application/json") {
// // return response.data;
// // }
// data = await new Response(data).json();
// }
// // 获取错误信息
if (!data) {
// 返回“[HTTP]请求没有返回值”;
throw new Error();
}
if (
response.request.responseType === 'blob' ||
response.request.responseType === 'arraybuffer'
) {
return response;
// data = await new Response(data).json();
}
// 获取错误信息
// const msg = data.msg || errorCode[code] || errorCode["default"];
// if (ignoreMsgs.indexOf(msg) !== -1) {
// // 如果是忽略的错误码,直接返回 msg 异常

View File

@@ -1,4 +1,5 @@
import { request } from "@umijs/max";
import { message } from "antd";
export interface SampleVo {
/**
@@ -85,9 +86,11 @@ export interface AiSampleRespVO {
* 样本时长
*/
sampleTime?: string;
tags?: { tagName?: string; id?: number }[];
}
export interface SampleReqVo extends PageParam {
current: number | undefined;
name?: string;
status?: number;
}
@@ -244,3 +247,73 @@ export const updateSampleTag = async (params: {
data: params,
});
};
// 下载
export const downloadSample = async (ids: number[]) => {
return request("/ai/sample/download", {
method: "GET",
params: { ids },
responseType: "blob",
});
};
export async function downloadZipFile(ids: number[]) {
try {
const response = await downloadSample(ids);
const blob = response; // Blob {size: 3164203, type: 'application/zip'}
// 验证文件类型
if (blob.type !== "application/zip" && !blob.type.includes("zip")) {
console.warn("返回的可能不是 ZIP 文件:", blob.type);
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = downloadUrl;
link.download = `音频文件_${new Date().getTime()}.wav`; // 设置文件名和后缀
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
return;
}
// 从响应头获取文件名
let fileName = "音频文件";
const contentDisposition =
response.response?.headers?.["content-disposition"];
if (!fileName && contentDisposition) {
const match = contentDisposition.match(
/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
);
if (match?.[1]) {
fileName = decodeURIComponent(match[1].replace(/['"]/g, ""));
}
}
// 默认文件名
fileName = fileName || `archive_${new Date().getTime()}.zip`;
// 创建下载链接
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = downloadUrl;
link.download = fileName;
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
message.success(
`下载成功,文件大小:${(blob.size / 1024 / 1024).toFixed(2)} MB`
);
return true;
} catch (error) {
console.error("下载失败:", error);
message.error("下载失败,请稍后重试");
return false;
}
}