Skip to content

Commit 3ef7066

Browse files
committed
refactor(spx-gui): refact the import & export to the manager
1 parent 8cc303c commit 3ef7066

File tree

5 files changed

+430
-322
lines changed

5 files changed

+430
-322
lines changed

spx-gui/src/components/editor/common/painter/painter.vue

Lines changed: 37 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,6 @@
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'
236231
import FillTool from './components/fill_tool.vue'
237232
import TextTool from './components/text_tool.vue'
238233
import 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'
242234
import { canvasEventDelegator,type ToolHandler } from './utils/delegator'
235+
import { createImportExportManager, type ImportExportManager } from './utils/import-export-manager'
243236
244237
// 工具类型
245238
type ToolType = 'line' | 'brush' | 'reshape' | 'eraser' | 'rectangle' | 'circle' | 'fill' | 'text'
246239
247-
248240
// 响应式变量
249241
const canvasRef = ref<HTMLCanvasElement | null>(null)
250242
const canvasWidth = ref<number>(800)
@@ -255,9 +247,9 @@ const currentTool = ref<ToolType | null>(null)
255247
const drawLineRef = ref<InstanceType<typeof DrawLine> | null>(null)
256248
const drawBrushRef = ref<InstanceType<typeof DrawBrush> | null>(null)
257249
const 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
//事件分发器
263255
const 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
// 选择工具
275282
const selectTool = (tool: ToolType): void => {
276283
currentTool.value = tool
@@ -298,11 +305,8 @@ const handleMouseUp = (): void => {
298305
canvasEventDelegator.delegateGlobalMouseUp()
299306
}
300307
301-
302-
303308
// 状态管理
304309
const allPaths = ref<paper.Path[]>([])
305-
306310
// 存储背景图片的引用
307311
const backgroundImage = ref<paper.Raster | null>(null)
308312
// 背景矩形(用于导出时隐藏)
@@ -313,7 +317,6 @@ const isImportingFromProps = ref<boolean>(false)
313317
const isFirstMount = ref<boolean>(true)
314318
// 保存当前选中的路径,用于导入后恢复控制点显示
315319
const selectedPathForRestore = ref<paper.Path | null>(null)
316-
317320
// AI生成弹窗状态
318321
const 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)
338333
const 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接口给直线组件
406359
const getAllPathsValue = (): paper.Path[] => {
407360
return allPaths.value
@@ -413,7 +366,6 @@ const setAllPathsValue = (paths: paper.Path[]): void => {
413366
provide('getAllPathsValue',getAllPathsValue)
414367
provide('setAllPathsValue',setAllPathsValue)
415368
416-
417369
// 处理笔刷路径创建
418370
const 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生成弹窗
432382
const 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
// 清空画布
507432
const 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
640545
provide('currentTool', currentTool)
641546
provide('reshapeRef', reshapeRef)
642547
provide('backgroundRect', backgroundRect)
643548
provide('isImportingFromProps', isImportingFromProps)
644549
provide('exportSvgAndEmit', exportSvgAndEmit)
645-
646-
// 为图片加载组件提供必要的依赖
647-
provide('backgroundImage', backgroundImage)
648550
</script>
649551

650552
<style scoped>

spx-gui/src/components/editor/common/painter/utils/ImageLoader.vue

Lines changed: 0 additions & 43 deletions
This file was deleted.

0 commit comments

Comments
 (0)