Repository navigation
feat: chat export - #7914
Merged
Merged
feat: chat export#7914
Conversation
shaohuzhang1
commented
Nov 13, 2026
| <el-button type="primary" :loading="loading" @click="save('png')">导出图片</el-button> | ||
| </template> | ||
| </MkDialog> | ||
| </template> |
Contributor
Author
There was a problem hiding this comment.
Review of the code
Overall the logic is sound. Below are irregularities, potential issues, and optimization suggestions.
1. Potential issues (correctness / robustness)
a) toDataURL on a tainted canvas
- In
open(),canvas.toDataURL('image/png')can throw aSecurityErrorif the canvas contains cross-origin images/fonts that weren't drawn with CORS. This isn't wrapped in try/catch, so it would surface as an uncaught error. Consider wrapping and showingMsgError.
b) PDF pagination crops content at page boundaries
- Slicing by
pagePixelsmeans a row of pixels can be cut mid-line across two pages. If content ends up taller thanpagePixels, it will overlap/duplicate visually. A common approach: scale the whole image to page width and let jsPDF handle multi-page viaautoPaging, or add small margins. At minimum document this behavior. - Also
(page.height * width) / page.widthuseswidth(page width mm), which is correct, but for the last partial page the image height is smaller — that's fine, but there's no vertical margin.
c) JPEG quality loss
- Using
'image/jpeg', 0.95for PDF introduces compression artifacts and makes text look blurry. For screenshots/logs, consider PNG ('image/png') for sharper text, at the cost of larger file size. Or make it configurable.
d) Re-entrancy of loading guard
if (!canvas || loading.value) returnguards concurrent saves, but since both buttons shareloading, clicking one disables both — expected here, but noteel-buttonwith the sameloadingprop means during one export the other button also shows spinning. Acceptable, just be aware.
e) fileName not sanitized
- If
namecomes from user/conversation content, it may contain/ \ : * ? " < > |etc., breaking downloads on some OSes. Sanitize before use.
f) Large canvas memory
- Creating one
<canvas>per page in a loop is fine, but for very long conversations this could allocate many large canvases. They become GC-eligible each iteration (no reference kept), so it's OK. HowevertoDataURLon each page is synchronous and blocks the UI. ConsidertoBlob+ async conversion, or chunk withawait new Promise(r => setTimeout(r))between pages to keep UI responsive.
g) capturedCanvas retained after close
clear()on@closedandonBeforeUnmountnulls it — good. ButpreviewUrl(a full base64 data URL) stays in memory until close; that's expected. EnsureMkDialogactually emitsclosed; if it only supportsv-modelupdate,clearmight never run. Verify the component contract.
h) alt / accessibility
- The preview
<img>hasalt— good. The dialog itself could use a more descriptive label, but that depends onMkDialog.
2. Minor / style
previewUrl = canvas.toDataURL()duplicates work:save('png')callscanvas.toBlobagain. Not a bug, just redundant.- Magic numbers
0.95,'image/jpeg'could be named constants. - Error message
'图片生成失败'and'无法创建画布'aren't surfaced to the user (the catch swallows them). Log the actual error for debugging:} catch (err) { console.error(err) MsgError('导出失败,请稍后重试') }
- The
forloop could computetotalPages = Math.ceil(canvas.height / pagePixels)first for clarity and to reuse in progress display.
3. Optimization suggestions
- Use
toBlobinstead oftoDataURLfor the final image inopen()if you don't strictly need a data URL — create an object URL (URL.createObjectURL(blob)) and revoke it inclear(). This avoids a large base64 string lingering in memory and is friendlier to memory for big canvases:function clear() { if (previewUrl.value.startsWith('blob:')) URL.revokeObjectURL(previewUrl.value) previewUrl.value = '' capturedCanvas = null }
- Yield to the event loop between pages when generating the PDF to keep the UI responsive on long conversations.
- Consider passing
jsPDFimage format based on content: PNG for text-heavy logs (sharpness), JPEG for photo-heavy. - Add a page margin so sliced pages don't run edge-to-edge and look cleaner.
Summary
No blocking bugs, but the main risks are: uncaught toDataURL security error, mid-line cropping in PDF pagination, unsanitized filename, swallowed error details, and synchronous blocking on large exports. Address those and the code is solid.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat: chat export