208208 :canvas-height =" canvasHeight"
209209 />
210210
211- <!-- SVG导入工具组件 -->
212- <SvgImporter ref =" svgImporterRef" />
213-
214- <!-- 图片加载工具组件 -->
215- <ImageLoader ref =" imageLoaderRef" />
216211 </div >
217212
218213 <!-- AI生成弹窗 -->
@@ -236,15 +231,12 @@ import CircleTool from './components/circle_tool.vue'
236231import FillTool from ' ./components/fill_tool.vue'
237232import TextTool from ' ./components/text_tool.vue'
238233import AiGenerate from ' ./components/aigc/generator.vue'
239- import SvgImporter from ' ./utils/SvgImporter.vue'
240- import { useSvgExporter } from ' ./utils/svg-exporter'
241- import ImageLoader from ' ./utils/ImageLoader.vue'
242234import { canvasEventDelegator ,type ToolHandler } from ' ./utils/delegator'
235+ import { createImportExportManager , type ImportExportManager } from ' ./utils/import-export-manager'
243236
244237// 工具类型
245238type ToolType = ' line' | ' brush' | ' reshape' | ' eraser' | ' rectangle' | ' circle' | ' fill' | ' text'
246239
247-
248240// 响应式变量
249241const canvasRef = ref <HTMLCanvasElement | null >(null )
250242const canvasWidth = ref <number >(800 )
@@ -255,9 +247,9 @@ const currentTool = ref<ToolType | null>(null)
255247const drawLineRef = ref <InstanceType <typeof DrawLine > | null >(null )
256248const drawBrushRef = ref <InstanceType <typeof DrawBrush > | null >(null )
257249const reshapeRef = ref <InstanceType <typeof Reshape > | null >(null )
258- const svgImporterRef = ref <InstanceType <typeof SvgImporter > | null >(null )
259- const imageLoaderRef = ref <InstanceType <typeof ImageLoader > | null >(null )
260250
251+ // 导入导出管理器
252+ let importExportManager: ImportExportManager | null = null
261253
262254// 事件分发器
263255const initEventDelegator = (): void => {
@@ -267,10 +259,25 @@ const initEventDelegator = (): void => {
267259 reshape: reshapeRef .value as ToolHandler ,
268260 })
269261
270-
271262 canvasEventDelegator .setCurrentTool (currentTool .value )
272263}
273264
265+ // 初始化导入导出管理器
266+ const initImportExportManager = (): void => {
267+ importExportManager = createImportExportManager ({
268+ canvasWidth ,
269+ canvasHeight ,
270+ canvasRef ,
271+ allPaths ,
272+ currentTool ,
273+ reshapeRef ,
274+ backgroundRect ,
275+ backgroundImage ,
276+ isImportingFromProps ,
277+ emit : (event : string , data : any ) => emit (event as any , data )
278+ })
279+ }
280+
274281// 选择工具
275282const selectTool = (tool : ToolType ): void => {
276283 currentTool .value = tool
@@ -298,11 +305,8 @@ const handleMouseUp = (): void => {
298305 canvasEventDelegator .delegateGlobalMouseUp ()
299306}
300307
301-
302-
303308// 状态管理
304309const allPaths = ref <paper .Path []>([])
305-
306310// 存储背景图片的引用
307311const backgroundImage = ref <paper .Raster | null >(null )
308312// 背景矩形(用于导出时隐藏)
@@ -313,7 +317,6 @@ const isImportingFromProps = ref<boolean>(false)
313317const isFirstMount = ref <boolean >(true )
314318// 保存当前选中的路径,用于导入后恢复控制点显示
315319const selectedPathForRestore = ref <paper .Path | null >(null )
316-
317320// AI生成弹窗状态
318321const aiDialogVisible = ref <boolean >(false )
319322
@@ -326,58 +329,10 @@ const emit = defineEmits<{
326329 (e : ' svg-change' , svg : string ): void
327330}>()
328331
329-
330- // 加载位图图片到画布(PNG/JPG/...)
331- const loadImageToCanvas = (imageSrc : string ): void => {
332- if (imageLoaderRef .value ) {
333- imageLoaderRef .value .loadImageToCanvas (imageSrc )
334- }
335- }
336-
337332// 根据 url 自动判断并导入到画布(优先解析为 SVG,其次作为位图 Raster)
338333const loadFileToCanvas = async (imageSrc : string ): Promise <void > => {
339- if (! paper .project ) return
340- try {
341- const resp = await fetch (imageSrc )
342- // 先尝试从响应体的 blob.type 判断(对 blob: URL 更可靠)
343- let isSvg = false
344- let svgText: string | null = null
345- try {
346- const blob = await resp .clone ().blob ()
347- if (blob && typeof blob .type === ' string' && blob .type .includes (' image/svg' )) {
348- isSvg = true
349- svgText = await blob .text ()
350- }
351- } catch {}
352-
353- // 退化到 header 判断
354- if (! isSvg ) {
355- const contentType = resp .headers .get (' content-type' ) || ' '
356- if (contentType .includes (' image/svg' )) {
357- isSvg = true
358- svgText = await resp .clone ().text ()
359- }
360- }
361-
362- // 最后尝试直接将文本解析为 SVG(针对部分 blob: 无类型场景)
363- if (! isSvg ) {
364- try {
365- const text = await resp .clone ().text ()
366- if (/ ^ \s * <svg[\s\S ] * <\/ svg>\s * $ / i .test (text )) {
367- isSvg = true
368- svgText = text
369- }
370- } catch {}
371- }
372-
373- if (isSvg && svgText != null ) {
374- importSvgToCanvas (svgText )
375- } else {
376- loadImageToCanvas (imageSrc )
377- }
378- } catch {
379- // 回退策略:按位图处理
380- loadImageToCanvas (imageSrc )
334+ if (importExportManager ) {
335+ await importExportManager .importFile (imageSrc )
381336 }
382337}
383338
@@ -400,8 +355,6 @@ const initPaper = (): void => {
400355 paper .view .update ()
401356}
402357
403-
404-
405358// painter提供allPath接口给直线组件
406359const getAllPathsValue = (): paper .Path [] => {
407360 return allPaths .value
@@ -413,7 +366,6 @@ const setAllPathsValue = (paths: paper.Path[]): void => {
413366provide (' getAllPathsValue' ,getAllPathsValue )
414367provide (' setAllPathsValue' ,setAllPathsValue )
415368
416-
417369// 处理笔刷路径创建
418370const handlePathCreated = (path : paper .Path ): void => {
419371 allPaths .value .push (path )
@@ -426,8 +378,6 @@ const handlePathsUpdate = (paths: paper.Path[]): void => {
426378 allPaths .value = paths
427379}
428380
429-
430-
431381// 显示AI生成弹窗
432382const showAiDialog = (): void => {
433383 aiDialogVisible .value = true
@@ -465,55 +415,24 @@ const handleAiCancel = (): void => {
465415}
466416
467417// 导入PNG图片到画布
468- const importImageToCanvas = (imageUrl : string ): void => {
469- if (! paper .project ) return
470-
471- const raster = new paper .Raster (imageUrl )
472-
473- raster .onLoad = () => {
474- raster .position = paper .view .center
475-
476- // 保持原始尺寸,不进行自动缩放
477- // 如果需要缩放,用户可以手动调整
478-
479- // 添加点击事件处理
480- // raster.onMouseDown = (event: paper.MouseEvent) => {
481- // if (currentTool.value === 'reshape') {
482- // selectPathExclusive(null) // 清除路径选择
483- // raster.selected = true
484- // paper.view.update()
485- // }
486- // }
487-
488- paper .view .update ()
489- // console.log('PNG图片已导入到画布')
490-
491- exportSvgAndEmit ()
492- }
493-
494- raster .onError = () => {
495- console .error (' failed to load image' )
418+ const importImageToCanvas = async (imageUrl : string ): Promise <void > => {
419+ if (importExportManager ) {
420+ await importExportManager .importImage (imageUrl )
496421 }
497422}
498423
499- // 导入SVG到画布并转换为可编辑的路径(通过SVG导入组件)
500- const importSvgToCanvas = (svgContent : string ): void => {
501- if (svgImporterRef . value ) {
502- svgImporterRef . value . importSvgToCanvas (svgContent )
424+ // 导入SVG到画布并转换为可编辑的路径
425+ const importSvgToCanvas = async (svgContent : string ): Promise < void > => {
426+ if (importExportManager ) {
427+ await importExportManager . importSvg (svgContent )
503428 }
504429}
505430
506431// 清空画布
507432const clearCanvas = (): void => {
508- if (reshapeRef . value ) {
509- reshapeRef . value . hideControlPoints ()
433+ if (importExportManager ) {
434+ importExportManager . clearCanvas ()
510435 }
511- allPaths .value .forEach ((path : paper .Path ) => {
512- if (path && path .parent ) {
513- path .remove ()
514- }
515- })
516- allPaths .value = []
517436
518437 // 清理选中路径状态
519438 selectedPathForRestore .value = null
@@ -527,21 +446,6 @@ const clearCanvas = (): void => {
527446 if (drawBrushRef .value ) {
528447 drawBrushRef .value .resetDrawing ()
529448 }
530-
531- paper .project .clear ()
532-
533- // 重新创建背景
534- const background = new paper .Path .Rectangle ({
535- point: [0 , 0 ],
536- size: [canvasWidth .value , canvasHeight .value ],
537- fillColor: ' transparent'
538- })
539- backgroundRect .value = background
540-
541- paper .view .update ()
542-
543- // 导出变更
544- exportSvgAndEmit ()
545449}
546450
547451// 监听props中的imgSrc变化
@@ -622,6 +526,7 @@ onMounted(() => {
622526 window .addEventListener (' keydown' , handleKeyDown )
623527 window .addEventListener (' mouseup' , handleMouseUp )
624528 initEventDelegator ()
529+ initImportExportManager ()
625530 // 清理函数
626531 onUnmounted (() => {
627532 canvasEventDelegator .clearToolRefs ()
@@ -630,21 +535,18 @@ onMounted(() => {
630535 })
631536})
632537
633- // 导出当前画布为 SVG 并上报父组件
634- const { exportSvgAndEmit } = useSvgExporter ( {
635- reshapeRef ,
636- backgroundRect ,
637- emit : ( svg : string ) => emit ( ' svg-change ' , svg )
638- })
538+ // 导出SVG的封装函数
539+ const exportSvgAndEmit = () : void => {
540+ if ( importExportManager ) {
541+ importExportManager . exportSvgAndEmit ()
542+ }
543+ }
639544
640545provide (' currentTool' , currentTool )
641546provide (' reshapeRef' , reshapeRef )
642547provide (' backgroundRect' , backgroundRect )
643548provide (' isImportingFromProps' , isImportingFromProps )
644549provide (' exportSvgAndEmit' , exportSvgAndEmit )
645-
646- // 为图片加载组件提供必要的依赖
647- provide (' backgroundImage' , backgroundImage )
648550 </script >
649551
650552<style scoped>
0 commit comments