Skip to content

Commit 03eef25

Browse files
committed
feat(pwa): M2 Task7 - 剪藏浏览器替代(fetch+DOMParser)+ PDF 明确降级提示
1 parent 15f7839 commit 03eef25

1 file changed

Lines changed: 30 additions & 10 deletions

File tree

‎client/src/features/notes/hooks/useClipImport.ts‎

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,47 @@ export function useClipImport() {
1818
const [clipLoading, setClipLoading] = useState(false);
1919

2020
const handleClipUrl = useCallback(async () => {
21-
if (!clipUrl.trim() || !window.electronAPI?.invoke) return;
21+
if (!clipUrl.trim()) return;
2222
setClipLoading(true);
2323
try {
24-
const result = await window.electronAPI.invoke('import:fetch-url', { url: clipUrl.trim() }) as { success: boolean; content?: { title: string; text: string }; error?: string };
25-
if (result.success && result.content) {
26-
const { title, text } = result.content;
27-
await createNote({ title: title.slice(0, 100), content: text, template: 'blank', folderId: selectedFolderId ?? undefined });
28-
toast({ type: 'success', message: `网页已剪藏为笔记:${title.slice(0, 30)}`, silent: true });
29-
setClipUrl('');
24+
let title = '';
25+
let text = '';
26+
if (window.electronAPI?.invoke) {
27+
// Electron:主进程 fetch(无 CORS 限制)+ 解析
28+
const result = await window.electronAPI.invoke('import:fetch-url', { url: clipUrl.trim() }) as { success: boolean; content?: { title: string; text: string }; error?: string };
29+
if (!result.success || !result.content) {
30+
toast({ type: 'warning', message: result.error || '剪藏失败,请手动粘贴内容' });
31+
return;
32+
}
33+
title = result.content.title;
34+
text = result.content.text;
3035
} else {
31-
toast({ type: 'warning', message: result.error || '剪藏失败,请手动粘贴内容' });
36+
// PWA/浏览器:直接 fetch + DOMParser 提取标题与正文(受 CORS 限制,
37+
// 目标站点不允许跨域时抛出并降级为手动粘贴提示)
38+
const resp = await fetch(clipUrl.trim(), { signal: AbortSignal.timeout(10000) });
39+
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
40+
const html = await resp.text();
41+
const doc = new DOMParser().parseFromString(html, 'text/html');
42+
title = doc.title || new URL(clipUrl.trim()).hostname;
43+
text = doc.body?.innerText?.trim() ?? '';
44+
if (!text) throw new Error('empty page');
3245
}
46+
await createNote({ title: title.slice(0, 100) || '未命名剪藏', content: text, template: 'blank', folderId: selectedFolderId ?? undefined });
47+
toast({ type: 'success', message: `网页已剪藏为笔记:${title.slice(0, 30)}`, silent: true });
48+
setClipUrl('');
3349
} catch {
34-
toast({ type: 'error', message: '剪藏失败,请检查网络或手动粘贴' });
50+
toast({ type: 'warning', message: '剪藏失败:该网站不允许直接抓取,请手动复制内容' });
3551
} finally {
3652
setClipLoading(false);
3753
}
3854
}, [clipUrl, createNote, selectedFolderId, toast]);
3955

4056
const handleClipPdf = useCallback(async () => {
41-
if (!window.electronAPI?.invoke) return;
57+
if (!window.electronAPI?.invoke) {
58+
// PWA/浏览器:明确降级提示(浏览器无内置 PDF 文本解析,MVP 阶段引导桌面端导入)
59+
toast({ type: 'warning', message: '移动端暂不支持 PDF 直接导入,请用桌面端导入或复制文本' });
60+
return;
61+
}
4262
setClipLoading(true);
4363
try {
4464
const result = await window.electronAPI.invoke('import:parse-pdf') as { success: boolean; content?: { title: string; text: string }; canceled?: boolean; error?: string };

0 commit comments

Comments
 (0)