topfans/frontend/composables/useLaserSegment.js
Lenticular Studio Agent af7908e72e feat: 接入微达API中转站,重构镭射卡生图流程
- 替换中转站从 xbcl.link 到 weda.cc
- prompt 模板改为镭射卡全图生成(去掉 6 层合成/抠图依赖)
- 4 路并发调用 + 原图展示 = 5 张 variant
- 前端提示词中译英支持
- 全局 Vue errorHandler
- WebSocket 鉴权失败跳登录
- 删除已弃用的 laserCompositor 微服务

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-23 22:43:49 +08:00

108 lines
3.0 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.

/**
* 镭射卡人像抠图 — 网关 POST /api/v1/segmentimageseg / VIAPI
* 抠图失败即抛错,中断 thinking不做椭圆降级。
*/
import { segmentPortraitApi } from '@/utils/api.js'
import { CASTLOVE_FORM_KEY } from '@/utils/castloveGenerationFlow.js'
async function downloadToLocal(url) {
const target = String(url || '').trim()
if (!target) {
throw new Error('缺少抠图下载地址')
}
// H5OSS 跨域时 uni.downloadFile 易丢 PNG alpha改用 fetch + blob URL
// #ifdef H5
const response = await fetch(target)
if (!response.ok) {
throw new Error(`下载抠图失败 HTTP ${response.status}`)
}
const blob = await response.blob()
return URL.createObjectURL(blob)
// #endif
// #ifndef H5
return new Promise((resolve, reject) => {
uni.downloadFile({
url: target,
timeout: 120000,
success: (res) => {
if (res.statusCode === 200 && res.tempFilePath) {
resolve(res.tempFilePath)
return
}
reject(new Error(`下载抠图失败 HTTP ${res.statusCode}`))
},
fail: (e) => reject(new Error(e?.errMsg || '下载抠图失败')),
})
})
// #endif
}
/** 将抠图本地路径写入 castlove 表单,供 craftMintSubmit 上传 cutout 层 */
export function persistCutoutToForm(cutoutLocalPath, cutoutOssKey = '') {
try {
const raw = uni.getStorageSync(CASTLOVE_FORM_KEY)
if (!raw) return
const form = typeof raw === 'string' ? JSON.parse(raw) : { ...raw }
form.cutoutImage = cutoutLocalPath
if (cutoutOssKey) {
form.cutout_oss_key = cutoutOssKey
}
uni.setStorageSync(CASTLOVE_FORM_KEY, JSON.stringify(form))
} catch (e) {
console.warn('[useLaserSegment] persistCutoutToForm failed:', e)
}
}
export function clearCutoutFromForm() {
try {
const raw = uni.getStorageSync(CASTLOVE_FORM_KEY)
if (!raw) return
const form = typeof raw === 'string' ? JSON.parse(raw) : { ...raw }
delete form.cutoutImage
delete form.cutout_oss_key
uni.setStorageSync(CASTLOVE_FORM_KEY, JSON.stringify(form))
} catch (e) {
/* noop */
}
}
/**
* 服务端人像抠图
* @param {string} imagePath 用户原图本地路径
* @returns {Promise<{ localPath: string, ossKey: string }>}
*/
export async function segmentPortrait(imagePath, options = {}) {
const path = String(imagePath || '').trim()
if (!path) {
throw new Error('缺少原图')
}
const res = await segmentPortraitApi(path, {
imageBase64: options.imageBase64 || '',
})
const data = res?.data
// 只要原图上传到 OSS 就算成功抠图失败不影响OpenAI 模式不需要抠图)
// 只有连原图都没上传成功才抛错
if (!data?.success && !data?.original_url_signed) {
throw new Error(data?.message || data?.error_code || 'LC_SEGMENT_FAILED')
}
// 抠图成功才下载存本地
let localPath = ''
if (data.cutout_url_signed) {
localPath = await downloadToLocal(data.cutout_url_signed)
persistCutoutToForm(localPath, data.cutout_oss_key || '')
}
return {
localPath,
ossKey: data.cutout_oss_key || '',
cutoutUrl: data.cutout_url_signed || '',
originalUrl: data.original_url_signed || '',
}
}