diff --git a/src/frontend/platform/src/controllers/API/linsight.ts b/src/frontend/platform/src/controllers/API/linsight.ts
index 27b59fee6..68b2cf0ea 100644
--- a/src/frontend/platform/src/controllers/API/linsight.ts
+++ b/src/frontend/platform/src/controllers/API/linsight.ts
@@ -44,4 +44,7 @@ export const sopApi = {
SyncSopRecord:(data) => {
return axios.post(`/api/v1/linsight/sop/record/sync`, data);
},
+ UploadSopRecord:(data) => {
+ return axios.post(`/api/v1/linsight/sop/upload`, data);
+ },
}
\ No newline at end of file
diff --git a/src/frontend/platform/src/controllers/request.ts b/src/frontend/platform/src/controllers/request.ts
index 9c061a1b1..19567eb63 100644
--- a/src/frontend/platform/src/controllers/request.ts
+++ b/src/frontend/platform/src/controllers/request.ts
@@ -23,6 +23,9 @@ customAxios.interceptors.request.use(function (config) {
customAxios.interceptors.response.use(function (response) {
if (response.data.status_code === 200) {
return response.data.data;
+ }
+ if (response.data.status_code === 11010) {
+ return response.data;
}
const i18Msg = i18next.t(`errors.${response.data.status_code}`)
const errorMessage = i18Msg === `errors.${response.data.status_code}` ? response.data.status_message : i18Msg
diff --git a/src/frontend/platform/src/pages/BuildPage/bench/LingSiWork.tsx b/src/frontend/platform/src/pages/BuildPage/bench/LingSiWork.tsx
index 6626ad00e..a5f406f1e 100644
--- a/src/frontend/platform/src/pages/BuildPage/bench/LingSiWork.tsx
+++ b/src/frontend/platform/src/pages/BuildPage/bench/LingSiWork.tsx
@@ -2,7 +2,7 @@
import { Button } from "@/components/bs-ui/button";
import { Card, CardContent } from "@/components/bs-ui/card";
import { toast, useToast } from "@/components/bs-ui/toast/use-toast";
-
+import { AlertTriangle } from "lucide-react";
import SopFormDrawer from "@/components/LinSight/SopFormDrawer";
import ImportFromRecordsDialog from "@/components/LinSight/SopFromRecord";
import SopTable from "@/components/LinSight/SopTable";
@@ -21,6 +21,12 @@ import { useNavigate } from "react-router-dom";
import { FormInput } from "./FormInput";
import { Model } from "./ModelManagement";
import Preview from "./Preview";
+import { useDropzone } from "react-dropzone";
+import { useTranslation } from "react-i18next";
+import { locationContext } from "@/contexts/locationContext";
+import { UploadIcon } from "@/components/bs-icons";
+import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/bs-ui/dialog";
+import { captureAndAlertRequestErrorHoc } from "@/controllers/request";
export interface FormErrors {
@@ -101,6 +107,17 @@ export default function index({ formData: parentFormData, setFormData: parentSet
const [manuallyExpandedItems, setManuallyExpandedItems] = useState
([]);
const [initialized, setInitialized] = useState(false);
const [importDialogOpen, setImportDialogOpen] = useState(false);
+ const [localFileDialogOpen, setLocalFileDialogOpen] = useState(false);
+const [importFile, setImportFile] = useState(null);
+const [importFiles, setImportFiles] = useState([]);
+const [isImporting, setIsImporting] = useState(false);
+const [validationDialog, setValidationDialog] = useState({
+ open: false,
+ status_message: "共计划导入0条SOP,格式正确0条,错误0条"
+});
+ const [importFilesData, setImportFilesData] = useState([]);
+ const [duplicateNames, setDuplicateNames] = useState([]);
+ const [duplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
const [deleteConfirmModal, setDeleteConfirmModal] = useState({
open: false,
title: '确认删除',
@@ -156,6 +173,8 @@ export default function index({ formData: parentFormData, setFormData: parentSet
api: [],
mcp: []
});
+const [importFormData, setImportFormData] = useState(null);
+
const fetchTools = async (type: 'builtin' | 'api' | 'mcp') => {
setLoading(true);
try {
@@ -225,13 +244,13 @@ export default function index({ formData: parentFormData, setFormData: parentSet
.filter(Boolean);
}, [toolsData, activeToolTab, toolSearchTerm]);
- const refreshSopList = () => {
- fetchData({
- page: page,
- pageSize: 10,
- keyword: keywords
- });
-};
+// const refreshSopList = () => {
+// fetchData({
+// page: page,
+// pageSize: 10,
+// keyword: keywords
+// });
+// };
const fetchData = async (params: {
page: number;
pageSize: number;
@@ -243,7 +262,7 @@ export default function index({ formData: parentFormData, setFormData: parentSet
const res = await sopApi.getSopList({
page_size: params.pageSize,
page: params.page,
- keywords: params.keyword || keywords,
+ keywords: params.keyword || '',
sort: params.sort,
});
@@ -384,23 +403,25 @@ export default function index({ formData: parentFormData, setFormData: parentSet
const [selectedItems, setSelectedItems] = useState([]);
const [sortConfig, setSortConfig] = useState({ key: null, direction: '' });
- const handleSearch = (keyword: string, resetPage: boolean = false) => {
- const newKeywords = keyword;
- const newPage = resetPage || newKeywords.trim() === '' ? 1 : page;
+ const { t } = useTranslation()
+ const { appConfig } = useContext(locationContext)
+ const handleSearch = (keyword: string, resetPage: boolean = false) => {
+ const newKeywords = keyword;
+ const newPage = resetPage || newKeywords.trim() === '' ? 1 : page;
- fetchData({
- keyword: newKeywords,
- page: newPage,
- pageSize
- });
+ fetchData({
+ keyword: newKeywords,
+ page: newPage,
+ pageSize
+ });
- // 更新状态
- setKeywords(newKeywords);
- setPageInputValue(newPage.toString());
- if (newPage !== page) {
- setPage(newPage);
- }
- };
+ // 更新状态
+ setKeywords(newKeywords);
+ setPageInputValue(newPage.toString());
+ if (newPage !== page) {
+ setPage(newPage);
+ }
+};
// 关闭弹窗刷新下数据
useEffect(() => {
@@ -687,6 +708,80 @@ export default function index({ formData: parentFormData, setFormData: parentSet
return [...tools];
});
}, []);
+const { getRootProps: getLocalFileRootProps, getInputProps: getLocalFileInputProps } = useDropzone({
+ accept: {
+ 'application/*': appConfig.enableEtl4lm ?
+ ['.PDF', '.TXT', '.DOCX', '.PPT', '.PPTX', '.MD', '.HTML', '.XLS', '.XLSX', '.CSV', '.DOC', '.PNG', '.JPG', '.JPEG', '.BMP']
+ : ['.PDF', '.TXT', '.DOCX', '.DOC', '.PPT', '.PPTX', '.MD', '.HTML', '.XLS', '.XLSX', '.CSV']
+ },
+ multiple: true, // 允许选择多个文件
+ onDrop: (acceptedFiles) => {
+ setImportFiles(prev => [...prev, ...acceptedFiles]); // 追加新文件
+ }
+});
+const handleLocalFileImport = async () => {
+ setIsImporting(true);
+ try {
+ const formData = new FormData();
+ formData.append('file', importFiles[0]);
+
+ setImportFilesData([importFiles[0]]); // 只保存一个文件
+ const res = await sopApi.UploadSopRecord(formData);
+ console.log('API Response:', res); // 调试用
+ if(res.status_code === 11010){
+ setValidationDialog({
+ open: true,
+ status_message: res.status_message
+ });
+ }
+ if(res.status_code === 200){
+ const formData = new FormData();
+ importFilesData.forEach(file => {
+ formData.append('file', file);
+ });
+ formData.append('ignore_error', 'false');
+ formData.append('override', 'false');
+ formData.append('save_new', 'false');
+ const res = await sopApi.UploadSopRecord(formData);
+ if(res?.repeat_name){
+ setImportDialogOpen(true)
+ setDuplicateNames(res.repeat_name);
+ setDuplicateDialogOpen(true);
+ setImportFormData(formData);
+ }else{
+ toast({ variant: 'success', description: '提交成功' });
+ }
+ }
+
+ } finally {
+ setIsImporting(false);
+ }
+};
+const handleValidationDialogConfirm = async () => {
+ console.log(111);
+ const formData = new FormData();
+ importFilesData.forEach(file => {
+ formData.append('file', file);
+ });
+ formData.append('ignore_error', 'true');
+ formData.append('override', 'false');
+ formData.append('save_new', 'false');
+
+ setValidationDialog(prev => ({...prev, open: false}));
+
+
+
+ const res = await sopApi.UploadSopRecord(formData);
+ if(res?.repeat_name){
+ setImportDialogOpen(true)
+ setDuplicateNames(res.repeat_name);
+ setDuplicateDialogOpen(true);
+ setImportFormData(formData);
+ return
+ }else{
+ toast({ variant: 'success', description: '提交成功' });
+ }
+};
return (
{loading && (
@@ -744,7 +839,7 @@ export default function index({ formData: parentFormData, setFormData: parentSet
onChange={(e) => {
const newValue = e.target.value;
setKeywords(newValue);
- handleSearch(newValue, newValue.trim() === '');
+ handleSearch(newValue);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
@@ -760,12 +855,20 @@ export default function index({ formData: parentFormData, setFormData: parentSet
+
+
+
+