web_ppt / frontend /src /services /dataSyncService.ts
CatPtain's picture
Update frontend/src/services/dataSyncService.ts
3713bef verified
raw
history blame
8.72 kB
import api from '@/services'
import { debounce } from 'lodash'
// 添加前端截图支持
import { toPng, toJpeg, toSvg } from 'html-to-image'
class DataSyncService {
private currentPPTId: string | null = null
private saveTimeout: number | null = null
private isOnline = true
private autoSaveDelay = 300000 // 默认5分钟,可配置
private isInitialized = false
private debouncedSave: any = null
constructor() {
this.setupNetworkMonitoring()
}
// 延迟初始化,在 Pinia 可用后调用
async initialize() {
if (this.isInitialized) return
await this.setupAutoSave()
this.isInitialized = true
}
// 设置自动保存延迟时间(毫秒)
setAutoSaveDelay(delay: number) {
this.autoSaveDelay = Math.max(500, delay) // 最小500ms
if (this.isInitialized) {
this.setupAutoSave() // 重新设置自动保存
}
}
// 获取当前自动保存延迟时间
getAutoSaveDelay(): number {
return this.autoSaveDelay
}
// 设置当前PPT ID
setCurrentPPTId(pptId: string) {
this.currentPPTId = pptId
}
// 自动保存功能
private async setupAutoSave() {
if (this.debouncedSave) {
this.debouncedSave.cancel()
}
this.debouncedSave = debounce(async () => {
await this.savePPT()
}, this.autoSaveDelay) // 使用可配置的延迟时间
// 监听slides变化
try {
const { useSlidesStore } = await import('@/store')
const slidesStore = useSlidesStore()
slidesStore.$subscribe(() => {
if (this.isOnline && this.currentPPTId) {
this.debouncedSave()
}
})
}
catch (error) {
// console.warn('无法设置自动保存,store 未就绪:', error)
}
}
// 网络状态监控
private setupNetworkMonitoring() {
window.addEventListener('online', () => {
this.isOnline = true
// console.log('网络已连接,恢复自动保存')
})
window.addEventListener('offline', () => {
this.isOnline = false
// console.log('网络已断开,暂停自动保存')
})
}
// 保存PPT到后端
async savePPT(force = false): Promise<boolean> {
// 动态导入 store,避免初始化时的依赖问题
const { useAuthStore, useSlidesStore } = await import('@/store')
try {
const authStore = useAuthStore()
const slidesStore = useSlidesStore()
if (!authStore.isLoggedIn) {
// console.warn('用户未登录,无法保存')
return false
}
// 如果没有当前PPT ID且是强制保存,创建新PPT
if (!this.currentPPTId && force) {
try {
const response = await api.createPPT(slidesStore.title || '未命名演示文稿')
this.currentPPTId = response.pptId
// 更新slides store中的数据以匹配新创建的PPT
if (response.ppt) {
slidesStore.setSlides(response.ppt.slides)
slidesStore.setTitle(response.ppt.title)
slidesStore.setTheme(response.ppt.theme)
}
// console.log('创建新PPT并保存成功')
return true
}
catch (createError) {
// console.error('创建新PPT失败:', createError)
return false
}
}
if (!this.currentPPTId && !force) {
// console.warn('没有当前PPT ID')
return false
}
const pptData = {
pptId: this.currentPPTId,
title: slidesStore.title,
slides: slidesStore.slides,
theme: slidesStore.theme,
// 添加关键的尺寸信息
viewportSize: slidesStore.viewportSize,
viewportRatio: slidesStore.viewportRatio
}
await api.savePPT(pptData)
// console.log('PPT保存成功')
return true
}
catch (error) {
// console.error('PPT保存失败:', error)
return false
}
}
// 创建新PPT
async createNewPPT(title: string): Promise<string | null> {
const { useAuthStore } = await import('@/store')
const authStore = useAuthStore()
if (!authStore.isLoggedIn) {
throw new Error('用户未登录')
}
try {
const response = await api.createPPT(title)
this.setCurrentPPTId(response.pptId)
return response.pptId
}
catch (error) {
// console.error('创建PPT失败:', error)
throw error
}
}
// 加载PPT
async loadPPT(pptId: string): Promise<boolean> {
const { useAuthStore, useSlidesStore } = await import('@/store')
const authStore = useAuthStore()
const slidesStore = useSlidesStore()
if (!authStore.isLoggedIn) {
throw new Error('用户未登录')
}
try {
const pptData = await api.getPPT(pptId)
slidesStore.setSlides(pptData.slides)
slidesStore.setTitle(pptData.title)
if (pptData.theme) {
slidesStore.setTheme(pptData.theme)
}
this.setCurrentPPTId(pptId)
// console.log('PPT加载成功')
return true
}
catch (error) {
// console.error('PPT加载失败:', error)
throw error
}
}
// 获取PPT列表
async getPPTList() {
const { useAuthStore } = await import('@/store')
const authStore = useAuthStore()
if (!authStore.isLoggedIn) {
throw new Error('用户未登录')
}
return await api.getPPTList()
}
// 删除PPT
async deletePPT(pptId: string): Promise<boolean> {
const { useAuthStore } = await import('@/store')
const authStore = useAuthStore()
if (!authStore.isLoggedIn) {
throw new Error('用户未登录')
}
try {
await api.deletePPT(pptId)
// 如果删除的是当前PPT,清除当前PPT ID
if (this.currentPPTId === pptId) {
this.currentPPTId = null
}
// console.log('PPT删除成功')
return true
}
catch (error) {
// console.error('PPT删除失败:', error)
throw error
}
}
// 生成分享链接
async generateShareLink(slideIndex = 0) {
const { useAuthStore } = await import('@/store')
const authStore = useAuthStore()
if (!authStore.isLoggedIn || !this.currentPPTId) {
throw new Error('用户未登录或没有当前PPT')
}
try {
const response = await api.generateShareLink(
authStore.currentUser!.id,
this.currentPPTId,
slideIndex
)
return response
}
catch (error) {
// console.error('生成分享链接失败:', error)
throw error
}
}
// 手动保存
async manualSave(): Promise<boolean> {
return await this.savePPT(true)
}
// 前端截图功能 - 作为后端截图的备用方案
async generateFrontendScreenshot(elementId = 'slideContainer', options = {}) {
try {
const element = document.getElementById(elementId)
if (!element) {
throw new Error(`Element with id '${elementId}' not found`)
}
const defaultOptions = {
quality: 0.95,
pixelRatio: 1,
backgroundColor: '#ffffff',
...options
}
console.log('🖼️ 开始前端截图生成...')
// 使用html-to-image生成截图
const imageDataUrl = await toJpeg(element, defaultOptions)
console.log('✅ 前端截图生成成功')
return imageDataUrl
} catch (error) {
console.error('❌ 前端截图生成失败:', error)
throw error
}
}
// 生成PPT截图链接(优先使用后端,失败时尝试前端)
async generateScreenshotUrl(slideIndex = 0, useFrontend = false) {
const { useAuthStore } = await import('@/store')
const authStore = useAuthStore()
if (!authStore.isLoggedIn || !this.currentPPTId) {
throw new Error('用户未登录或没有当前PPT')
}
const baseUrl = window.location.origin
const backendScreenshotUrl = `${baseUrl}/api/public/screenshot/${authStore.currentUser!.id}/${this.currentPPTId}/${slideIndex}`
if (useFrontend) {
try {
// 尝试前端截图
const frontendScreenshot = await this.generateFrontendScreenshot()
return {
type: 'frontend',
url: frontendScreenshot,
isDataUrl: true
}
} catch (frontendError) {
console.warn('前端截图失败,回退到后端URL:', frontendError.message)
return {
type: 'backend',
url: backendScreenshotUrl,
isDataUrl: false
}
}
}
return {
type: 'backend',
url: backendScreenshotUrl,
isDataUrl: false
}
}
}
// 创建单例实例
export const dataSyncService = new DataSyncService()
export default dataSyncService