mirror of
https://github.com/dataelement/bisheng.git
synced 2026-08-30 17:58:00 +08:00
merge: 同步远端 feat/2.5.0-sg
This commit is contained in:
@@ -696,7 +696,7 @@ function FileTableHeader({
|
||||
{localize("com_knowledge.file_size")}</SortableHeader>
|
||||
|
||||
<TableHead
|
||||
className="relative bg-[#F3F4F6] p-0 text-right font-normal text-[15px] text-[#545A60]"
|
||||
className="sticky top-0 z-20 bg-[#F3F4F6] p-0 font-normal text-[15px] text-[#545A60]"
|
||||
style={{ width: columnWidths.totalCount, minWidth: columnWidths.totalCount, maxWidth: columnWidths.totalCount }}
|
||||
>
|
||||
<div className="flex h-full items-center justify-end border-l pl-3 pr-3">
|
||||
|
||||
@@ -3096,16 +3096,15 @@
|
||||
|
||||
.infoEditModalLabelWrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.infoEditRenameHint {
|
||||
color: #f53f3f;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.infoEditModalOverlay {
|
||||
|
||||
@@ -258,7 +258,6 @@ export default function PortalKnowledgeWorkbench() {
|
||||
const [folderDraft, setFolderDraft] = useState("新建文件夹");
|
||||
const [activePanel, setActivePanel] = useState<PanelKey | null>(null);
|
||||
const [aiDrawerOpen, setAiDrawerOpen] = useState(false);
|
||||
const [openAiAfterDeepLinkRestore, setOpenAiAfterDeepLinkRestore] = useState(false);
|
||||
const [summaryExpanded, setSummaryExpanded] = useState(false);
|
||||
const [tagModalOpen, setTagModalOpen] = useState(false);
|
||||
const [permissionOpen, setPermissionOpen] = useState(false);
|
||||
@@ -455,6 +454,11 @@ export default function PortalKnowledgeWorkbench() {
|
||||
if (!selectedFile || String(selectedFile.id) !== portalDeepLinkTarget.fileId) return;
|
||||
if (preview.loading) return;
|
||||
setRestoringDeepLinkKey(null);
|
||||
// Deep-link file entry (e.g. "进入知识库") opens the AI drawer by default.
|
||||
// Search-result previews mark the URL with fromSearch=1 and keep it closed.
|
||||
if (portalDeepLinkTarget.fromSearch === "1") return;
|
||||
setAiDrawerOpen(true);
|
||||
setActivePanel(null);
|
||||
}, [
|
||||
portalDeepLinkTarget,
|
||||
preview.error,
|
||||
@@ -462,28 +466,6 @@ export default function PortalKnowledgeWorkbench() {
|
||||
preview.loading,
|
||||
restoringDeepLinkKey,
|
||||
selectedFile,
|
||||
]);
|
||||
|
||||
// Parent-driven "进入知识库" file deep links should open the AI drawer once restore finishes,
|
||||
// regardless of whether the file lives at the root or inside a sub-folder.
|
||||
useEffect(() => {
|
||||
if (!openAiAfterDeepLinkRestore) return;
|
||||
if (!portalDeepLinkTarget?.fileId || isDeepLinkRestoring) return;
|
||||
if (
|
||||
!selectedFile ||
|
||||
String(selectedFile.id) !== portalDeepLinkTarget.fileId ||
|
||||
String(selectedFile.spaceId) !== portalDeepLinkTarget.spaceId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setAiDrawerOpen(true);
|
||||
setActivePanel(null);
|
||||
setOpenAiAfterDeepLinkRestore(false);
|
||||
}, [
|
||||
openAiAfterDeepLinkRestore,
|
||||
isDeepLinkRestoring,
|
||||
portalDeepLinkTarget,
|
||||
selectedFile,
|
||||
setActivePanel,
|
||||
setAiDrawerOpen,
|
||||
]);
|
||||
@@ -1491,7 +1473,6 @@ export default function PortalKnowledgeWorkbench() {
|
||||
next.set("openNonce", openNonce);
|
||||
return next;
|
||||
}, { replace: true });
|
||||
setOpenAiAfterDeepLinkRestore(true);
|
||||
}
|
||||
};
|
||||
window.addEventListener("message", handlePortalMessage);
|
||||
@@ -2187,6 +2168,7 @@ export default function PortalKnowledgeWorkbench() {
|
||||
next.delete("folderName");
|
||||
next.delete("fileId");
|
||||
next.delete("fileName");
|
||||
next.delete("fromSearch");
|
||||
next.delete("documentId");
|
||||
next.delete("name");
|
||||
next.delete("openNonce");
|
||||
@@ -2271,12 +2253,20 @@ export default function PortalKnowledgeWorkbench() {
|
||||
const deepestFolder = parentFolders[parentFolders.length - 1];
|
||||
const folderId = deepestFolder?.id || currentFolderId || "";
|
||||
const folderName = deepestFolder?.name || currentFolderNode?.file.name || "";
|
||||
const isFromSearch =
|
||||
searchMode &&
|
||||
searchResults.some(
|
||||
(f) =>
|
||||
String(f.id) === String(selectedFile.id) &&
|
||||
String(f.spaceId) === String(selectedFile.spaceId),
|
||||
);
|
||||
if (
|
||||
portalDeepLinkTarget &&
|
||||
portalDeepLinkTarget.spaceId === spaceId &&
|
||||
portalDeepLinkTarget.folderId === folderId &&
|
||||
portalDeepLinkTarget.fileId === fileId &&
|
||||
portalDeepLinkTarget.fileName === fileName
|
||||
portalDeepLinkTarget.fileName === fileName &&
|
||||
portalDeepLinkTarget.fromSearch === (isFromSearch ? "1" : "")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -2293,6 +2283,11 @@ export default function PortalKnowledgeWorkbench() {
|
||||
}
|
||||
next.set("fileId", fileId);
|
||||
next.set("fileName", fileName);
|
||||
if (isFromSearch) {
|
||||
next.set("fromSearch", "1");
|
||||
} else {
|
||||
next.delete("fromSearch");
|
||||
}
|
||||
next.delete("documentId");
|
||||
next.delete("name");
|
||||
next.delete("openNonce");
|
||||
@@ -2304,6 +2299,8 @@ export default function PortalKnowledgeWorkbench() {
|
||||
currentFolderId,
|
||||
currentFolderNode?.file.name,
|
||||
portalDeepLinkTarget,
|
||||
searchMode,
|
||||
searchResults,
|
||||
selectedFile,
|
||||
selectedFileParentPath,
|
||||
setSearchParams,
|
||||
@@ -2313,7 +2310,6 @@ export default function PortalKnowledgeWorkbench() {
|
||||
setSelectedFile(null);
|
||||
setActivePanel(null);
|
||||
setAiDrawerOpen(false);
|
||||
setOpenAiAfterDeepLinkRestore(false);
|
||||
setSummaryExpanded(false);
|
||||
setPreview({ loading: false, fileUrl: "", fileType: "", error: "", previewData: null });
|
||||
// Deep-link open used to leave searchMode with a single-file result set.
|
||||
@@ -2348,6 +2344,7 @@ export default function PortalKnowledgeWorkbench() {
|
||||
}
|
||||
next.delete("fileId");
|
||||
next.delete("fileName");
|
||||
next.delete("fromSearch");
|
||||
next.delete("documentId");
|
||||
next.delete("name");
|
||||
next.delete("openNonce");
|
||||
|
||||
+1
-1
@@ -311,7 +311,7 @@ export function PortalFileInfoEditModal({
|
||||
<div className={s.infoEditModalLabelWrap}>
|
||||
<label className={s.infoEditModalLabel}>文件名称</label>
|
||||
<span className={s.infoEditRenameHint}>
|
||||
* 编辑完成之后进行回车保存
|
||||
* 编辑完成之后回车进行保存
|
||||
</span>
|
||||
</div>
|
||||
<div className={s.infoEditModalField}>
|
||||
|
||||
@@ -18,6 +18,8 @@ export interface PortalDeepLinkTarget {
|
||||
fileName: string;
|
||||
/** Forces re-apply when the same file is opened again via postMessage. */
|
||||
openNonce: string;
|
||||
/** Set to "1" when the file was selected from the global search results. */
|
||||
fromSearch?: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
@@ -60,6 +62,7 @@ export const resolvePortalDeepLinkTarget = (searchParams: URLSearchParams): Port
|
||||
const fileId = getQueryValue(searchParams, ["fileId", "documentId", "document_id"]);
|
||||
const fileName = getQueryValue(searchParams, ["name", "fileName", "documentName", "document_name"]);
|
||||
const openNonce = getQueryValue(searchParams, ["openNonce", "open_nonce"]);
|
||||
const fromSearch = getQueryValue(searchParams, ["fromSearch", "from_search"]);
|
||||
return {
|
||||
spaceId,
|
||||
folderId,
|
||||
@@ -67,8 +70,9 @@ export const resolvePortalDeepLinkTarget = (searchParams: URLSearchParams): Port
|
||||
fileId,
|
||||
fileName,
|
||||
openNonce,
|
||||
fromSearch,
|
||||
// openNonce lets postMessage re-open the same file after the preview was closed.
|
||||
key: `${spaceId}:${folderId}:${folderName}:${fileId}:${fileName}:${openNonce}`,
|
||||
key: `${spaceId}:${folderId}:${folderName}:${fileId}:${fileName}:${openNonce}:${fromSearch}`,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -32,14 +32,14 @@ export function SourceFileLinks({ files, max = 3 }: { files: TagConsoleSourceFil
|
||||
const rest = files.length - shown.length
|
||||
|
||||
return (
|
||||
<div className="flex min-w-0 flex-col gap-0.5">
|
||||
<div className="flex w-full min-w-0 flex-col gap-0.5">
|
||||
{shown.map((file) => {
|
||||
if (isViolationFile(file)) {
|
||||
return (
|
||||
<button
|
||||
key={file.file_id}
|
||||
type="button"
|
||||
className="block w-full truncate text-left text-[#F53F3F] hover:underline"
|
||||
className="block w-full min-w-0 truncate text-left text-[#F53F3F] hover:underline"
|
||||
title={t("build.tagConsole.violationBlocked", "该文件包含违规内容,无法预览")}
|
||||
onClick={() => setViolating(file)}
|
||||
>
|
||||
@@ -50,7 +50,7 @@ export function SourceFileLinks({ files, max = 3 }: { files: TagConsoleSourceFil
|
||||
const url = buildTagFileDetailUrl(file)
|
||||
if (!url) {
|
||||
return (
|
||||
<span key={file.file_id} className="block truncate" title={file.file_name}>
|
||||
<span key={file.file_id} className="block w-full min-w-0 truncate" title={file.file_name}>
|
||||
{file.file_name}
|
||||
</span>
|
||||
)
|
||||
@@ -62,7 +62,7 @@ export function SourceFileLinks({ files, max = 3 }: { files: TagConsoleSourceFil
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title={file.file_name}
|
||||
className="block truncate text-blue-600 hover:underline"
|
||||
className="block w-full min-w-0 truncate text-blue-600 hover:underline"
|
||||
>
|
||||
{file.file_name}
|
||||
</a>
|
||||
|
||||
+5
-5
@@ -114,25 +114,25 @@ export function TagReviewDialog({ target, libraries, saving, onClose, onApprove,
|
||||
}
|
||||
|
||||
const field = (label: string, value: React.ReactNode) => (
|
||||
<div className="flex items-start gap-3 py-1 text-sm">
|
||||
<div className="flex min-w-0 items-start gap-3 py-1 text-sm">
|
||||
<span className="w-28 shrink-0 leading-6 text-muted-foreground">{label}</span>
|
||||
<span className="min-w-0 flex-1 overflow-hidden leading-6">{value}</span>
|
||||
<span className="w-0 min-w-0 flex-1 overflow-hidden leading-6">{value}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<Dialog open={Boolean(target)} onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent className="gap-0 overflow-hidden p-0 sm:max-w-[640px] bg-background-login">
|
||||
<DialogContent className="w-full max-w-[calc(100vw-2rem)] gap-0 p-0 sm:max-w-[640px] bg-background-login">
|
||||
<DialogHeader className="border-b border-[#EBECF0] px-6 py-4">
|
||||
<DialogTitle>{t("build.tagConsole.reviewTitle", "标签审核")}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 px-6 py-5">
|
||||
<div className="min-w-0 space-y-4 px-6 py-5">
|
||||
{loading ? (
|
||||
<p className="py-6 text-center text-sm text-muted-foreground">{t("loading")}</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="overflow-hidden rounded-lg border border-[#ECECEC] bg-[#FAFBFC] p-4">
|
||||
<div className="min-w-0 overflow-hidden rounded-lg border border-[#ECECEC] bg-[#FAFBFC] p-4">
|
||||
{field(t("build.tagName", "标签名称"), detail?.name || "-")}
|
||||
{field(
|
||||
t("build.tagConsole.tagType", "标签类型"),
|
||||
|
||||
Reference in New Issue
Block a user