- NotebookAIWritingPage reads `token` from URL, fetches space data from notebook-token API (search_space_id, doc_ids, user_token) and space name from searchspaces API; keep-alive ping every 50s resets Redis TTL - Pass user_token as studioToken through AIWritingPage → AIWritingLayout → WritingFormPanel → AIWritingPanel → AIWritingForm to skip getTokenByEmail when studio token is already available - useStudioNotebooks accepts enabled flag; useNotebookSources accepts overrideToken to bypass the internal token fetch - AIWritingForm hides notebook selector when studioToken is provided (notebook is pre-locked) and pre-selects initialDocumentIds Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
111 lines
3.8 KiB
TypeScript
111 lines
3.8 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { useSearchParams } from 'react-router-dom';
|
|
import AIWritingPage from '@/open-canvas/pages/AIWritingPage';
|
|
import { getNotebookApiBaseURL } from '@/core/config';
|
|
|
|
interface NotebookTokenData {
|
|
doc_ids: number[];
|
|
search_space_id: number;
|
|
user_id: string;
|
|
user_token: string;
|
|
}
|
|
|
|
export default function NotebookAIWritingPage() {
|
|
const [searchParams] = useSearchParams();
|
|
const token = searchParams.get('token');
|
|
|
|
const [data, setData] = useState<NotebookTokenData | null>(null);
|
|
const [spaceName, setSpaceName] = useState<string | null>(null);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!token) {
|
|
setError('缺少 token 参数');
|
|
return;
|
|
}
|
|
const base = getNotebookApiBaseURL();
|
|
fetch(`${base}/api/v1/ai-writing/notebook-token/${token}`)
|
|
.then((res) => {
|
|
if (!res.ok) throw new Error(`请求失败 ${res.status}`);
|
|
return res.json() as Promise<NotebookTokenData>;
|
|
})
|
|
.then((d) => {
|
|
setData(d);
|
|
return fetch(`${base}/api/v1/searchspaces/${d.search_space_id}`, {
|
|
headers: { Authorization: `Bearer ${d.user_token}` },
|
|
});
|
|
})
|
|
.then((res) => {
|
|
if (!res.ok) return null;
|
|
return res.json() as Promise<{ name: string }>;
|
|
})
|
|
.then((space) => {
|
|
if (space?.name) setSpaceName(space.name);
|
|
})
|
|
.catch((e: Error) => setError(e.message));
|
|
}, [token]);
|
|
|
|
// Keep-alive:每 50 秒 ping 一次 token 接口,防止 Redis TTL 过期
|
|
useEffect(() => {
|
|
if (!token) return;
|
|
const base = getNotebookApiBaseURL();
|
|
const id = setInterval(() => {
|
|
fetch(`${base}/api/v1/ai-writing/notebook-token/${token}`).catch(() => {});
|
|
}, 50_000);
|
|
return () => clearInterval(id);
|
|
}, [token]);
|
|
|
|
return (
|
|
<div className="h-full flex flex-col overflow-hidden">
|
|
{/* 索财笔记 Header */}
|
|
<div className="flex items-center gap-3 px-5 h-11 shrink-0 bg-gradient-to-r from-[#1e3a5f] to-[#1a3255] border-b border-[#2a4a70]">
|
|
{/* 品牌 */}
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-6 h-6 rounded-md bg-blue-500 flex items-center justify-center text-white text-xs font-bold shrink-0">
|
|
索
|
|
</div>
|
|
<span className="text-[#e0f0ff] text-sm font-semibold tracking-wide">索财笔记</span>
|
|
</div>
|
|
|
|
{/* 分隔线 */}
|
|
<div className="w-px h-4 bg-[#2d4f7a]" />
|
|
|
|
{/* 空间名称 */}
|
|
{data?.search_space_id && (
|
|
<div className="flex items-center gap-1.5">
|
|
<span className="text-[#7fb3e8] text-xs">空间</span>
|
|
<span className="text-[#b3d4f5] text-xs font-semibold bg-[#1a3a60] border border-[#2d5080] rounded px-2 py-0.5">
|
|
{spaceName ?? data.search_space_id}
|
|
</span>
|
|
</div>
|
|
)}
|
|
|
|
{/* 弹性占位 */}
|
|
<div className="flex-1" />
|
|
|
|
{/* 笔记标签 */}
|
|
<div className="flex items-center gap-1.5 bg-[#0f2840] border border-[#2a4a70] rounded-md px-2.5 py-1">
|
|
<span className="text-xs">📒</span>
|
|
<span className="text-[#bfdbfe] text-xs">这是我的笔记</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 主体 */}
|
|
<div className="flex-1 min-h-0 overflow-hidden">
|
|
{error ? (
|
|
<div className="flex items-center justify-center h-full text-red-400 text-sm">{error}</div>
|
|
) : !data ? (
|
|
<div className="flex items-center justify-center h-full text-[#7fb3e8] text-sm">加载中…</div>
|
|
) : (
|
|
<AIWritingPage
|
|
hideAdvancedFields
|
|
initialNotebookId={data.search_space_id}
|
|
initialDocumentIds={data.doc_ids}
|
|
studioToken={data.user_token}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|