fix(export): 相对导出三道保险(E3 三轮)
未保存文档禁用相对模式并说明原因;保存对话框默认打开到文档所在 文件夹;导出后按 statFile 核查正文图片在文档旁是否存在,缺失弹窗报数, 不再静默裂图。
This commit is contained in:
38
src/App.tsx
38
src/App.tsx
@@ -824,16 +824,48 @@ export default function App() {
|
||||
})
|
||||
}, [docOverrides, ui.docTheme, tauriMode, resolveExportImage])
|
||||
|
||||
/** 相对导出后核查:正文引用的本地图片是否真实存在于文档旁,缺失数返回给提示。 */
|
||||
const countMissingImages = useCallback(async (docPath: string, content: string): Promise<number> => {
|
||||
const srcs = new Set<string>()
|
||||
for (const m of content.matchAll(/!\[[^\]]*\]\(([^)\s]+)/g)) srcs.add(m[1])
|
||||
let missing = 0
|
||||
for (const src of srcs) {
|
||||
if (/^(https?:|data:)/i.test(src)) continue
|
||||
let decoded = src
|
||||
try { decoded = decodeURIComponent(src) } catch { /* 保持原样 */ }
|
||||
try {
|
||||
if (!(await statFile(joinPath(dirOf(docPath), decoded))).exists) missing++
|
||||
} catch { missing++ }
|
||||
}
|
||||
return missing
|
||||
}, [])
|
||||
|
||||
const exportHtmlFile = useCallback(async (imageMode: ImageMode) => {
|
||||
if (!tab) return
|
||||
setExportOpen(false)
|
||||
try {
|
||||
if (tauriMode) {
|
||||
// 先定落点再生成:相对模式要按 HTML 文件所在目录重写图片路径(验收 E3)
|
||||
const target = await pickSaveHtmlFile(`${tab.title}.html`)
|
||||
// 相对模式的前提是文档已落盘:没有磁盘路径就无从定位图片(验收 E3 三轮)
|
||||
if (imageMode === 'relative' && !tab.path) {
|
||||
setError('当前文档尚未保存到磁盘,相对路径模式无法定位图片;请先保存文档,或改用「图片内嵌」模式导出')
|
||||
return
|
||||
}
|
||||
// 先定落点再生成:相对模式要按 HTML 文件所在目录重写图片路径(验收 E3);
|
||||
// 保存对话框默认打开到文档所在文件夹,同目录导出时相对路径天然成立
|
||||
const defaultTarget = imageMode === 'relative' && tab.path
|
||||
? joinPath(dirOf(tab.path), `${tab.title}.html`)
|
||||
: `${tab.title}.html`
|
||||
const target = await pickSaveHtmlFile(defaultTarget)
|
||||
if (!target) return
|
||||
const html = await buildExportHtml(tab, imageMode, { exportDir: dirOf(target) })
|
||||
await writeTextFile(target, html, false)
|
||||
if (imageMode === 'relative' && tab.path) {
|
||||
const missing = await countMissingImages(tab.path, tab.content)
|
||||
if (missing > 0) {
|
||||
flash(`已导出 HTML,但有 ${missing} 张图片在文档旁未找到,相对链接会失效(先确认编辑器里图片是否正常显示)`)
|
||||
return
|
||||
}
|
||||
}
|
||||
flash(`已导出 HTML:${baseName(target)}`)
|
||||
} else {
|
||||
const html = await buildExportHtml(tab, imageMode)
|
||||
@@ -841,7 +873,7 @@ export default function App() {
|
||||
flash('已导出 HTML(浏览器下载)')
|
||||
}
|
||||
} catch (e) { setError(String(e)) }
|
||||
}, [tab, buildExportHtml, tauriMode, flash])
|
||||
}, [tab, buildExportHtml, tauriMode, flash, countMissingImages])
|
||||
|
||||
const copyHtml = useCallback(async () => {
|
||||
if (!tab) return
|
||||
|
||||
Reference in New Issue
Block a user