topfans/frontend/composables/useLaserBatchGenerate.js

142 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 镭射卡五图批量生成Phase 1纯客户端 Canvas无后端接口
*/
import { ref } from 'vue'
import {
CASTLOVE_FORM_KEY,
consumeGenerationFlowPayload,
persistLaserPreviewImages,
} from '@/utils/castloveGenerationFlow.js'
import { generateLaserVariantBatch } from '@/utils/laser-card/laserBatchExport.js'
const DEFAULT_CANVAS_ID = 'laserBatchCanvas'
const DEFAULT_MIN_DURATION_MS = 1600
/**
* @param {Object} [options]
* @param {string} [options.canvasId]
* @param {number} [options.minDurationMs]
* @param {number} [options.flowStartedAt] 页面 onMounted 时的时间戳,用于最短展示时长
*/
export function useLaserBatchGenerate(options = {}) {
const progress = ref(0)
const running = ref(false)
const error = ref(null)
let progressTimer = null
let flowStartedAt = options.flowStartedAt ?? Date.now()
const canvasId = options.canvasId || DEFAULT_CANVAS_ID
const minDurationMs = options.minDurationMs ?? DEFAULT_MIN_DURATION_MS
const stopProgress = () => {
if (progressTimer) {
clearInterval(progressTimer)
progressTimer = null
}
}
const simulateProgress = () => {
stopProgress()
progressTimer = setInterval(() => {
if (progress.value < 90) {
const increment = Math.random() * 5 + 2
progress.value = Math.min(90, progress.value + increment)
}
}, 500)
}
const completeProgress = () =>
new Promise((resolve) => {
stopProgress()
const finalInterval = setInterval(() => {
if (progress.value < 100) {
progress.value = Math.min(100, progress.value + 5)
} else {
clearInterval(finalInterval)
setTimeout(resolve, 500)
}
}, 50)
})
const waitMinDuration = async (minMs) => {
if (!minMs || minMs <= 0) return
const elapsed = Date.now() - flowStartedAt
if (elapsed < minMs) {
await new Promise((r) => setTimeout(r, minMs - elapsed))
}
}
const readFormData = () => {
const formStr = uni.getStorageSync(CASTLOVE_FORM_KEY)
if (!formStr) {
throw new Error('缺少表单数据')
}
return JSON.parse(formStr)
}
const resolveImagePath = (formData) => {
const imagePath = formData?.image || formData?.uploadedImage || ''
if (!imagePath) {
throw new Error('缺少上传图片')
}
return imagePath
}
/**
* 执行五图合成并写入 Storage
* @param {Object} [runOptions]
* @param {Object} [runOptions.flow] consumeGenerationFlowPayload 结果;缺省则自动 consume
* @returns {Promise<string[]>} 本地临时 JPG 路径列表
*/
const run = async (runOptions = {}) => {
if (running.value) {
throw new Error('生成进行中')
}
running.value = true
error.value = null
simulateProgress()
try {
const flow = runOptions.flow ?? consumeGenerationFlowPayload()
const minMs = flow?.minDurationMs ?? minDurationMs
const formData = readFormData()
const imagePath = resolveImagePath(formData)
const paths = await generateLaserVariantBatch(imagePath, canvasId)
await waitMinDuration(minMs)
persistLaserPreviewImages(paths)
await completeProgress()
return paths
} catch (e) {
error.value = e
stopProgress()
throw e
} finally {
running.value = false
}
}
const resetProgress = () => {
stopProgress()
progress.value = 0
}
const setFlowStartedAt = (ts) => {
flowStartedAt = ts
}
return {
progress,
running,
error,
simulateProgress,
completeProgress,
stopProgress,
resetProgress,
setFlowStartedAt,
run,
}
}