Merge branch 'feat/2.0.0' of github.com:dataelement/bisheng into feat/2.0.0

This commit is contained in:
xiejiayu
2025-08-11 18:08:54 +08:00
8 changed files with 70 additions and 40 deletions
@@ -229,17 +229,22 @@ class SOPManageService:
name = sheet.cell(row=i, column=1).value
description = sheet.cell(row=i, column=2).value
content = sheet.cell(row=i, column=3).value
error_msg = []
if not name:
error_rows.append(f"• 第{i}行: 缺少名称")
elif not content:
error_rows.append(f"• 第{i}行: 缺少详细内容内容")
elif len(str(name)) >= 500:
error_rows.append(f"• 第{i}行: 名称长度超过500字符")
elif len(str(content)) >= 50000:
error_rows.append(f"• 第{i}行: 详细内容长度超过50000字符")
elif description and len(str(description)) >= 1000:
error_rows.append(f"• 第{i}行: 描述长度超过1000字符")
elif name and content:
error_msg.append("缺少名称")
if not content:
error_msg.append("缺少详细内容")
if len(str(name)) >= 500:
error_msg.append("名称长度超过500字符")
if len(str(content)) >= 50000:
error_msg.append("详细内容长度超过50000字符")
if description and len(str(description)) >= 1000:
error_msg.append("描述长度超过1000字符")
if error_msg:
error_msg = "".join(error_msg)
error_rows.extend(f"• 第{i}行: {error_msg}")
else:
success_rows.append({
"name": str(name),
"description": str(description) if description is not None else "",
@@ -46,7 +46,7 @@ class LinsightAgent(BaseModel):
knowledge_list_str = ""
if knowledge_list:
knowledge_list_str = "\n".join(knowledge_list)
knowledge_list_str = f"<知识库列表>\n{knowledge_list_str}\n</<知识库列表>>"
knowledge_list_str = f"<知识库列表>\n{knowledge_list_str}\n</知识库列表>"
return knowledge_list_str
async def _parse_sop_content(self, sop_prompt: str) -> AsyncIterator[ChatGenerationChunk]:
@@ -129,14 +129,14 @@ const SopFormDrawer = ({
<div className="flex flex-col ">
<div className="flex items-center justify-between px-4 pt-5 border-gray-200">
<SheetTitle className="text-lg font-medium text-gray-900">
{isEditing ? '编辑SOP' : '新建SOP'}
{isEditing ? '编辑指导手册' : '新建指导手册'}
</SheetTitle>
</div>
<div className="flex-1 px-4 pb-4 pt-3">
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="sop-name" className="block text-sm font-medium pb-1 text-gray-700">
SOP名称<span className="text-red-500">*</span>
<span className="text-red-500">*</span>
</label>
< Input
type="text"
@@ -147,7 +147,7 @@ const SopFormDrawer = ({
value={sopForm.name}
onChange={(e) => handleInputChange('name', e.target.value)}
className={`mt-1 block w-full border ${errors.name ? 'border-red-500' : 'border-gray-300'} rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-[16px]`}
placeholder="请输入SOP名称"
placeholder="请输入指导手册名称"
/>
<div className="flex justify-between">
{errors.name && (
@@ -167,7 +167,7 @@ const SopFormDrawer = ({
value={sopForm.description}
onChange={(e) => handleInputChange('description', e.target.value)}
className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-blue-500 focus:border-blue-500 text-[16px]"
placeholder="请输入SOP描述"
placeholder="请输入指导手册描述"
/>
</div>
@@ -247,14 +247,14 @@ const fetchRecords = async (isSearch = false) => {
{/* 左侧记录列表 */}
<div className="p-6 w-[50%] min-w-160">
<SheetHeader>
<SheetTitle>SOP</SheetTitle>
<SheetTitle></SheetTitle>
</SheetHeader>
<div className="relative mt-6 mb-6 w-[80%]">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<input
type="text"
placeholder="搜索SOP"
placeholder="搜索指导手册"
className="w-full pl-10 pr-4 py-2 rounded-md border focus:outline-none focus:ring-2 focus:ring-primary"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
@@ -466,7 +466,7 @@ const fetchRecords = async (isSearch = false) => {
}}
disabled={!currentRecord || loading}
>
{loading ? '导入中...' : '导入当前SOP'}
{loading ? '导入中...' : '导入当前指导手册'}
</Button>
</div>
</>
@@ -1,7 +1,7 @@
// src/features/chat-config/ChatConfig.tsx
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 { message, 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";
@@ -27,6 +27,8 @@ 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";
import sampleData from "@/pages/ModelPage/components/sampleData";
import { downloadJson } from "@/util/utils";
export interface FormErrors {
@@ -709,14 +711,17 @@ const [importFormData, setImportFormData] = useState<FormData | null>(null);
});
}, []);
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, // 允许选择多个文件
multiple: false,
onDrop: (acceptedFiles) => {
setImportFiles(prev => [...prev, ...acceptedFiles]); // 追加新文件
if (acceptedFiles.length === 0) return;
const file = acceptedFiles[0];
const ext = file.name.split('.').pop().toLowerCase();
if (ext !== 'xlsx') {
message({ variant: 'warning', description: '请上传xlsx格式的文件' });
return;
}
setImportFiles(acceptedFiles);
}
});
const handleLocalFileImport = async () => {
@@ -749,7 +754,13 @@ const handleLocalFileImport = async () => {
setDuplicateDialogOpen(true);
setImportFormData(formData);
}else{
sopApi.getSopList({
page_size: pageSize,
page: 1,
keywords: keywords,
})
toast({ variant: 'success', description: '提交成功' });
}
}
@@ -779,6 +790,11 @@ const handleValidationDialogConfirm = async () => {
setImportFormData(formData);
return
}else{
sopApi.getSopList({
page_size: pageSize,
page: 1,
keywords: keywords,
})
toast({ variant: 'success', description: '提交成功' });
}
};
@@ -827,13 +843,13 @@ const handleValidationDialogConfirm = async () => {
</div>
<div className="mb-6">
<p className="text-lg font-bold mb-2">SOP库</p>
<p className="text-lg font-bold mb-2"></p>
<div className="flex items-center gap-2 mb-2">
<div className="relative flex-1 max-w-xs">
<div className="relative">
<input
type="text"
placeholder="搜索SOP"
placeholder="搜索指导手册"
className="w-full pl-10 pr-3 py-1.5 text-sm border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
value={keywords}
onChange={(e) => {
@@ -883,7 +899,7 @@ const handleValidationDialogConfirm = async () => {
setIsDrawerOpen(true);
}}
>
SOP
</Button>
<Button
variant="outline"
@@ -976,7 +992,7 @@ const handleValidationDialogConfirm = async () => {
<Dialog open={localFileDialogOpen} onOpenChange={setLocalFileDialogOpen}>
<DialogContent className="sm:max-w-[1200px]">
<DialogHeader>
<DialogTitle>SOP</DialogTitle>
<DialogTitle></DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
@@ -985,23 +1001,23 @@ const handleValidationDialogConfirm = async () => {
<span></span>
<button
className="text-blue-600 hover:underline ml-auto"
onClick={() => downloadJson(sampleData)}
>
<span className="text-black"></span>
SOP导入格式示例.xlsx
.xlsx
</button>
</div>
<div
{...getLocalFileRootProps()}
className="group h-48 border border-dashed rounded-md flex flex-col justify-center items-center cursor-pointer gap-3 hover:border-primary"
className="group h-40 border border-dashed rounded-md flex flex-col justify-center items-center cursor-pointer gap-3 hover:border-primary"
>
<input {...getLocalFileInputProps()} />
<UploadIcon className="group-hover:text-primary size-5" />
<p className="text-sm">{t('code.clickOrDragHere')}</p>
{importFiles.length > 0 && (
<div className="w-full max-h-32 overflow-y-auto">
{importFiles.slice(0, 1).map((file, index) => ( // 只显示一个文件
{importFiles.slice(0, 1).map((file, index) => (
<div key={index} className="flex items-center justify-between p-1 bg-gray-200 mt-14">
<span className="text-sm text-gray-600 truncate max-w-xs">
{file.name}
@@ -1009,7 +1025,7 @@ const handleValidationDialogConfirm = async () => {
<button
onClick={(e) => {
e.stopPropagation();
setImportFiles([]); // 清空文件列表
setImportFiles([]);
}}
className="text-red-500 hover:text-red-700"
>
@@ -1035,6 +1051,7 @@ const handleValidationDialogConfirm = async () => {
<Button
onClick={async () => {
await handleLocalFileImport(); // 等待导入完成
setImportFiles([])
setLocalFileDialogOpen(false); // 关闭弹窗
}}
disabled={isImporting || importFiles.length === 0}
@@ -20,6 +20,9 @@ export const WebSearchConfig = ({
onChange: (field: string, value: any) => void;
errors?: Record<string, any>;
}) => {
console.log(config,88);
// Store parameters for all tools separately
const [toolsParams, setToolsParams] = useState<Record<string, any>>({
bing: { api_key: '', base_url: '' },
@@ -31,6 +34,7 @@ export const WebSearchConfig = ({
// Initialize toolsParams with existing config
useEffect(() => {
console.log(config,333);
if (config.tool && config.params) {
setToolsParams(prev => ({
...prev,
@@ -54,7 +58,7 @@ export const WebSearchConfig = ({
<Label className="bisheng-label"></Label>
<div className="mt-3">
<Textarea
value={config.prompt}
value={config.prompt||config}
className="min-h-48"
onChange={(e) => onChange('prompt', e.target.value)}
/>
@@ -370,8 +370,9 @@ const handleWebSearchChange = useCallback((field: string, value: any) => {
</Button>
}
>
{console.log(1111,webSearchData, formData.webSearch.prompt)}
<WebSearchConfig
config={webSearchData || formData.webSearch}
config={ formData.webSearch.prompt}
onChange={handleWebSearchChange}
/>
</ToggleSection>
@@ -426,6 +427,8 @@ const handleWebSearchChange = useCallback((field: string, value: any) => {
<DialogTitle></DialogTitle>
</DialogHeader>
<WebSearchForm
prompt={ formData.webSearch.prompt}
enabled={formData.webSearch.enabled}
formData={formData.webSearch}
onSubmit={handleWebSearchSave}
/>
@@ -29,7 +29,7 @@ const defaultToolParams = {
},
};
const WebSearchForm = ({ formData, onSubmit, errors = {} }) => {
const WebSearchForm = ({ formData, onSubmit, errors = {},enabled,prompt }) => {
const { t } = useTranslation();
const { config: webSearchData, setConfig } = useWebSearchStore();
const [loading, setLoading] = useState(true);
@@ -134,9 +134,10 @@ const WebSearchForm = ({ formData, onSubmit, errors = {} }) => {
}
const newConfig = {
enabled: true,
enabled,
type: selectedTool,
config: allToolsConfig
config: allToolsConfig,
prompt
};
try {
setConfig(newConfig);