Upload dataSyncService.ts
Browse files
frontend/src/services/dataSyncService.ts
CHANGED
@@ -392,6 +392,105 @@ class DataSyncService {
|
|
392 |
}
|
393 |
}
|
394 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
395 |
// Get PPT screenshot data (for frontend direct rendering)
|
396 |
async getScreenshotData(slideIndex = 0) {
|
397 |
const { useAuthStore } = await import('@/store')
|
|
|
392 |
}
|
393 |
}
|
394 |
|
395 |
+
// Generate image and upload to backend for external link access
|
396 |
+
async generateAndUploadImage(slideIndex = 0, options: any = {}) {
|
397 |
+
const { format = 'jpeg', quality = 90, elementSelector = '.slide-viewport, .canvas-viewport, .viewport-wrapper' } = options;
|
398 |
+
|
399 |
+
try {
|
400 |
+
const { useAuthStore, useSlidesStore } = await import('@/store')
|
401 |
+
const authStore = useAuthStore()
|
402 |
+
const slidesStore = useSlidesStore()
|
403 |
+
|
404 |
+
if (!authStore.isLoggedIn || !this.currentPPTId) {
|
405 |
+
throw new Error('User not logged in or no current PPT')
|
406 |
+
}
|
407 |
+
|
408 |
+
// Find target element for screenshot
|
409 |
+
let targetElement: HTMLElement | null = null
|
410 |
+
const selectors = elementSelector.split(',').map((s: string) => s.trim())
|
411 |
+
|
412 |
+
for (const selector of selectors) {
|
413 |
+
targetElement = document.querySelector(selector) as HTMLElement
|
414 |
+
if (targetElement) {
|
415 |
+
console.log(`✅ Found screenshot target element: ${selector}`)
|
416 |
+
break
|
417 |
+
}
|
418 |
+
}
|
419 |
+
|
420 |
+
if (!targetElement) {
|
421 |
+
throw new Error(`Screenshot target element not found. Tried selectors: ${selectors.join(', ')}`)
|
422 |
+
}
|
423 |
+
|
424 |
+
// Dynamically load html2canvas
|
425 |
+
const html2canvas = await this.loadHtml2Canvas()
|
426 |
+
|
427 |
+
console.log('🖼️ Starting to generate image for upload...', { format, quality })
|
428 |
+
|
429 |
+
// Wait for fonts and images to load
|
430 |
+
await this.ensureResourcesReady()
|
431 |
+
|
432 |
+
// Generate screenshot
|
433 |
+
const canvas = await html2canvas(targetElement, {
|
434 |
+
scale: 2, // High resolution
|
435 |
+
useCORS: true,
|
436 |
+
allowTaint: true,
|
437 |
+
backgroundColor: slidesStore.theme?.backgroundColor || '#ffffff',
|
438 |
+
logging: false,
|
439 |
+
onclone: function(clonedDoc) {
|
440 |
+
// Ensure correct fonts in cloned document
|
441 |
+
const clonedElements = clonedDoc.querySelectorAll('*')
|
442 |
+
clonedElements.forEach((el: any) => {
|
443 |
+
if (el.style) {
|
444 |
+
el.style.fontFamily = 'Microsoft YaHei, PingFang SC, Hiragino Sans GB, Source Han Sans SC, Noto Sans SC, WenQuanYi Micro Hei, SimHei, SimSun, Arial, Helvetica, sans-serif'
|
445 |
+
}
|
446 |
+
})
|
447 |
+
}
|
448 |
+
})
|
449 |
+
|
450 |
+
// Convert to specified format
|
451 |
+
const imageDataUrl = canvas.toDataURL(`image/${format}`, quality / 100)
|
452 |
+
|
453 |
+
console.log('✅ Image generated, uploading to backend...')
|
454 |
+
|
455 |
+
// Upload to backend
|
456 |
+
const baseUrl = window.location.origin
|
457 |
+
const uploadResponse = await fetch(`${baseUrl}/api/public/save-image/${authStore.currentUser!.id}/${this.currentPPTId}/${slideIndex}`, {
|
458 |
+
method: 'POST',
|
459 |
+
headers: {
|
460 |
+
'Content-Type': 'application/json'
|
461 |
+
},
|
462 |
+
body: JSON.stringify({
|
463 |
+
imageData: imageDataUrl,
|
464 |
+
format,
|
465 |
+
quality
|
466 |
+
})
|
467 |
+
})
|
468 |
+
|
469 |
+
if (!uploadResponse.ok) {
|
470 |
+
throw new Error(`Upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`)
|
471 |
+
}
|
472 |
+
|
473 |
+
const uploadResult = await uploadResponse.json()
|
474 |
+
|
475 |
+
console.log('✅ Image uploaded successfully, external URL:', uploadResult.imageUrl)
|
476 |
+
|
477 |
+
return {
|
478 |
+
success: true,
|
479 |
+
imageUrl: uploadResult.imageUrl,
|
480 |
+
imageId: uploadResult.imageId,
|
481 |
+
expiresAt: uploadResult.expiresAt,
|
482 |
+
format,
|
483 |
+
quality,
|
484 |
+
width: canvas.width,
|
485 |
+
height: canvas.height
|
486 |
+
}
|
487 |
+
|
488 |
+
} catch (error: any) {
|
489 |
+
console.error('❌ Failed to generate and upload image:', error)
|
490 |
+
throw error
|
491 |
+
}
|
492 |
+
}
|
493 |
+
|
494 |
// Get PPT screenshot data (for frontend direct rendering)
|
495 |
async getScreenshotData(slideIndex = 0) {
|
496 |
const { useAuthStore } = await import('@/store')
|