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])
|
}, [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) => {
|
const exportHtmlFile = useCallback(async (imageMode: ImageMode) => {
|
||||||
if (!tab) return
|
if (!tab) return
|
||||||
setExportOpen(false)
|
setExportOpen(false)
|
||||||
try {
|
try {
|
||||||
if (tauriMode) {
|
if (tauriMode) {
|
||||||
// 先定落点再生成:相对模式要按 HTML 文件所在目录重写图片路径(验收 E3)
|
// 相对模式的前提是文档已落盘:没有磁盘路径就无从定位图片(验收 E3 三轮)
|
||||||
const target = await pickSaveHtmlFile(`${tab.title}.html`)
|
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
|
if (!target) return
|
||||||
const html = await buildExportHtml(tab, imageMode, { exportDir: dirOf(target) })
|
const html = await buildExportHtml(tab, imageMode, { exportDir: dirOf(target) })
|
||||||
await writeTextFile(target, html, false)
|
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)}`)
|
flash(`已导出 HTML:${baseName(target)}`)
|
||||||
} else {
|
} else {
|
||||||
const html = await buildExportHtml(tab, imageMode)
|
const html = await buildExportHtml(tab, imageMode)
|
||||||
@@ -841,7 +873,7 @@ export default function App() {
|
|||||||
flash('已导出 HTML(浏览器下载)')
|
flash('已导出 HTML(浏览器下载)')
|
||||||
}
|
}
|
||||||
} catch (e) { setError(String(e)) }
|
} catch (e) { setError(String(e)) }
|
||||||
}, [tab, buildExportHtml, tauriMode, flash])
|
}, [tab, buildExportHtml, tauriMode, flash, countMissingImages])
|
||||||
|
|
||||||
const copyHtml = useCallback(async () => {
|
const copyHtml = useCallback(async () => {
|
||||||
if (!tab) return
|
if (!tab) return
|
||||||
|
|||||||
Reference in New Issue
Block a user