fix:创作添加修改角色名称
This commit is contained in:
parent
905f8d1b99
commit
c78af468d1
@ -5,9 +5,9 @@
|
|||||||
* 前端不持有 phone/orgId/appSecret,仅通过 JWT 认证调后端代理
|
* 前端不持有 phone/orgId/appSecret,仅通过 JWT 认证调后端代理
|
||||||
* 后端自动注入 orgId + phone 并使用 HMAC 签名
|
* 后端自动注入 orgId + phone 并使用 HMAC 签名
|
||||||
*/
|
*/
|
||||||
import OSS from 'ali-oss'
|
import OSS from "ali-oss";
|
||||||
import publicApi from '@/api/public'
|
import publicApi from "@/api/public";
|
||||||
import type { StsTokenData, CreateStoryParams } from './types'
|
import type { StsTokenData, CreateStoryParams } from "./types";
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════
|
||||||
// API 函数(全部走后端代理)
|
// API 函数(全部走后端代理)
|
||||||
@ -15,20 +15,23 @@ import type { StsTokenData, CreateStoryParams } from './types'
|
|||||||
|
|
||||||
/** 图片上传 */
|
/** 图片上传 */
|
||||||
export function uploadImage(file: File) {
|
export function uploadImage(file: File) {
|
||||||
const form = new FormData()
|
const form = new FormData();
|
||||||
form.append('file', file)
|
form.append("file", file);
|
||||||
return publicApi.post('/leai-proxy/upload', form, {
|
return publicApi.post("/leai-proxy/upload", form, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
headers: { "Content-Type": "multipart/form-data" },
|
||||||
timeout: 30000
|
timeout: 30000,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 角色提取 */
|
/** 角色提取 */
|
||||||
export function extractCharacters(imageUrl: string, opts: { saveOriginal?: boolean; title?: string } = {}) {
|
export function extractCharacters(
|
||||||
const body: Record<string, any> = { imageUrl }
|
imageUrl: string,
|
||||||
if (opts.saveOriginal) body.saveOriginal = true
|
opts: { saveOriginal?: boolean; title?: string } = {},
|
||||||
if (opts.title) body.title = opts.title
|
) {
|
||||||
return publicApi.post('/leai-proxy/extract', body, { timeout: 120000 })
|
const body: Record<string, any> = { imageUrl };
|
||||||
|
if (opts.saveOriginal) body.saveOriginal = true;
|
||||||
|
if (opts.title) body.title = opts.title;
|
||||||
|
return publicApi.post("/leai-proxy/extract", body, { timeout: 120000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 图片故事创作 */
|
/** 图片故事创作 */
|
||||||
@ -37,14 +40,15 @@ export function createStory(params: CreateStoryParams) {
|
|||||||
imageUrl: params.imageUrl,
|
imageUrl: params.imageUrl,
|
||||||
storyHint: params.storyHint,
|
storyHint: params.storyHint,
|
||||||
style: params.style,
|
style: params.style,
|
||||||
refAdaptMode: params.refAdaptMode || 'enhanced',
|
heroName: params.heroName,
|
||||||
enableVoice: false
|
refAdaptMode: params.refAdaptMode || "enhanced",
|
||||||
}
|
enableVoice: false,
|
||||||
if (params.title) body.title = params.title
|
};
|
||||||
if (params.author) body.author = params.author
|
if (params.title) body.title = params.title;
|
||||||
if (params.heroCharId) body.heroCharId = params.heroCharId
|
if (params.author) body.author = params.author;
|
||||||
if (params.extractId) body.extractId = params.extractId
|
if (params.heroCharId) body.heroCharId = params.heroCharId;
|
||||||
return publicApi.post('/leai-proxy/create-story', body)
|
if (params.extractId) body.extractId = params.extractId;
|
||||||
|
return publicApi.post("/leai-proxy/create-story", body);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,69 +56,67 @@ export function createStory(params: CreateStoryParams) {
|
|||||||
* 仍可能出现嵌套 data,统一解包为 Work 对象(与 CreatingView 原 detail.data 语义一致)。
|
* 仍可能出现嵌套 data,统一解包为 Work 对象(与 CreatingView 原 detail.data 语义一致)。
|
||||||
*/
|
*/
|
||||||
export function unwrapLeaiWorkDetail(raw: unknown): any {
|
export function unwrapLeaiWorkDetail(raw: unknown): any {
|
||||||
let cur: any = raw
|
let cur: any = raw;
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
if (!cur || typeof cur !== 'object') return cur
|
if (!cur || typeof cur !== "object") return cur;
|
||||||
if (cur.workId != null || Array.isArray(cur.pageList)) return cur
|
if (cur.workId != null || Array.isArray(cur.pageList)) return cur;
|
||||||
if (cur.data != null && typeof cur.data === 'object') {
|
if (cur.data != null && typeof cur.data === "object") {
|
||||||
cur = cur.data
|
cur = cur.data;
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
return cur
|
return cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查询作品详情 */
|
/** 查询作品详情 */
|
||||||
export function getWorkDetail(workId: string) {
|
export function getWorkDetail(workId: string) {
|
||||||
return publicApi
|
return publicApi.get(`/leai-proxy/work/${workId}`).then(unwrapLeaiWorkDetail);
|
||||||
.get(`/leai-proxy/work/${workId}`)
|
|
||||||
.then(unwrapLeaiWorkDetail)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 额度校验 */
|
/** 额度校验 */
|
||||||
export function checkQuota() {
|
export function checkQuota() {
|
||||||
return publicApi.post('/leai-proxy/validate', { apiType: 'A3' })
|
return publicApi.post("/leai-proxy/validate", { apiType: "A3" });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 编辑绘本信息 */
|
/** 编辑绘本信息 */
|
||||||
export function updateWork(workId: string, data: Record<string, any>) {
|
export function updateWork(workId: string, data: Record<string, any>) {
|
||||||
return publicApi.put(`/leai-proxy/work/${workId}`, data)
|
return publicApi.put(`/leai-proxy/work/${workId}`, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 批量更新配音 URL */
|
/** 批量更新配音 URL */
|
||||||
export function batchUpdateAudio(workId: string, pages: any[]) {
|
export function batchUpdateAudio(workId: string, pages: any[]) {
|
||||||
return publicApi.post('/leai-proxy/batch-audio', { workId, pages })
|
return publicApi.post("/leai-proxy/batch-audio", { workId, pages });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 完成配音 */
|
/** 完成配音 */
|
||||||
export function finishDubbing(workId: string) {
|
export function finishDubbing(workId: string) {
|
||||||
return batchUpdateAudio(workId, [])
|
return batchUpdateAudio(workId, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** AI 配音 */
|
/** AI 配音 */
|
||||||
export function voicePage(data: Record<string, any>) {
|
export function voicePage(data: Record<string, any>) {
|
||||||
return publicApi.post('/leai-proxy/voice', data, { timeout: 120000 })
|
return publicApi.post("/leai-proxy/voice", data, { timeout: 120000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** STS 临时凭证(经后端代理获取) */
|
/** STS 临时凭证(经后端代理获取) */
|
||||||
export function getStsToken() {
|
export function getStsToken() {
|
||||||
return publicApi.post('/leai-proxy/sts-token')
|
return publicApi.post("/leai-proxy/sts-token");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── OSS 直传 ──
|
// ─── OSS 直传 ──
|
||||||
let _ossClient: OSS | null = null
|
let _ossClient: OSS | null = null;
|
||||||
let _stsData: StsTokenData | null = null
|
let _stsData: StsTokenData | null = null;
|
||||||
|
|
||||||
async function getOssClient() {
|
async function getOssClient() {
|
||||||
if (_ossClient && _stsData) {
|
if (_ossClient && _stsData) {
|
||||||
const expireTime = new Date(_stsData.expiration).getTime()
|
const expireTime = new Date(_stsData.expiration).getTime();
|
||||||
if (Date.now() < expireTime - 5 * 60 * 1000) {
|
if (Date.now() < expireTime - 5 * 60 * 1000) {
|
||||||
return { client: _ossClient, prefix: _stsData.uploadPrefix }
|
return { client: _ossClient, prefix: _stsData.uploadPrefix };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const res = await getStsToken()
|
const res = await getStsToken();
|
||||||
_stsData = res as StsTokenData
|
_stsData = res as StsTokenData;
|
||||||
_ossClient = new OSS({
|
_ossClient = new OSS({
|
||||||
region: _stsData.region,
|
region: _stsData.region,
|
||||||
accessKeyId: _stsData.accessKeyId,
|
accessKeyId: _stsData.accessKeyId,
|
||||||
@ -123,17 +125,17 @@ async function getOssClient() {
|
|||||||
bucket: _stsData.bucket,
|
bucket: _stsData.bucket,
|
||||||
endpoint: _stsData.endpoint,
|
endpoint: _stsData.endpoint,
|
||||||
refreshSTSToken: async () => {
|
refreshSTSToken: async () => {
|
||||||
const r = await getStsToken()
|
const r = await getStsToken();
|
||||||
_stsData = r as StsTokenData
|
_stsData = r as StsTokenData;
|
||||||
return {
|
return {
|
||||||
accessKeyId: _stsData.accessKeyId,
|
accessKeyId: _stsData.accessKeyId,
|
||||||
accessKeySecret: _stsData.accessKeySecret,
|
accessKeySecret: _stsData.accessKeySecret,
|
||||||
stsToken: _stsData.securityToken
|
stsToken: _stsData.securityToken,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
refreshSTSTokenInterval: 300000
|
refreshSTSTokenInterval: 300000,
|
||||||
})
|
});
|
||||||
return { client: _ossClient, prefix: _stsData.uploadPrefix }
|
return { client: _ossClient, prefix: _stsData.uploadPrefix };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,35 +144,45 @@ async function getOssClient() {
|
|||||||
* @param opts 选项
|
* @param opts 选项
|
||||||
* @returns 文件的完整 OSS URL
|
* @returns 文件的完整 OSS URL
|
||||||
*/
|
*/
|
||||||
export async function ossUpload(file: File | Blob, opts: {
|
export async function ossUpload(
|
||||||
type?: string
|
file: File | Blob,
|
||||||
onProgress?: (pct: number) => void
|
opts: {
|
||||||
ext?: string
|
type?: string;
|
||||||
} = {}): Promise<string> {
|
onProgress?: (pct: number) => void;
|
||||||
const { type = 'img', onProgress, ext: forceExt } = opts
|
ext?: string;
|
||||||
const { client, prefix } = await getOssClient()
|
} = {},
|
||||||
const ext = forceExt || ((file as File).name ? (file as File).name.split('.').pop() : 'bin').toLowerCase()
|
): Promise<string> {
|
||||||
const date = new Date().toISOString().slice(0, 10)
|
const { type = "img", onProgress, ext: forceExt } = opts;
|
||||||
const rand = Math.random().toString(36).slice(2, 10)
|
const { client, prefix } = await getOssClient();
|
||||||
const key = `${prefix}${date}/${type}_${Date.now()}_${rand}.${ext}`
|
const ext =
|
||||||
|
forceExt ||
|
||||||
|
((file as File).name
|
||||||
|
? (file as File).name.split(".").pop()
|
||||||
|
: "bin"
|
||||||
|
).toLowerCase();
|
||||||
|
const date = new Date().toISOString().slice(0, 10);
|
||||||
|
const rand = Math.random().toString(36).slice(2, 10);
|
||||||
|
const key = `${prefix}${date}/${type}_${Date.now()}_${rand}.${ext}`;
|
||||||
await (client as OSS).put(key, file, {
|
await (client as OSS).put(key, file, {
|
||||||
headers: { 'x-oss-object-acl': 'public-read' },
|
headers: { "x-oss-object-acl": "public-read" },
|
||||||
progress: (p: number) => { if (onProgress) onProgress(Math.round(p * 100)) }
|
progress: (p: number) => {
|
||||||
})
|
if (onProgress) onProgress(Math.round(p * 100));
|
||||||
|
},
|
||||||
|
});
|
||||||
if (_stsData!.cdnDomain) {
|
if (_stsData!.cdnDomain) {
|
||||||
return `${_stsData!.cdnDomain}/${key}`
|
return `${_stsData!.cdnDomain}/${key}`;
|
||||||
}
|
}
|
||||||
return `https://${_stsData!.bucket}.${_stsData!.endpoint.replace('https://', '')}/${key}`
|
return `https://${_stsData!.bucket}.${_stsData!.endpoint.replace("https://", "")}/${key}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** STS 列举用户目录下的文件 */
|
/** STS 列举用户目录下的文件 */
|
||||||
export async function ossListFiles() {
|
export async function ossListFiles() {
|
||||||
const { client, prefix } = await getOssClient()
|
const { client, prefix } = await getOssClient();
|
||||||
const result = await (client as OSS).list({ prefix, 'max-keys': 100 })
|
const result = await (client as OSS).list({ prefix, "max-keys": 100 });
|
||||||
return (result.objects || []).map((obj: any) => ({
|
return (result.objects || []).map((obj: any) => ({
|
||||||
name: obj.name.replace(prefix, ''),
|
name: obj.name.replace(prefix, ""),
|
||||||
size: obj.size,
|
size: obj.size,
|
||||||
lastModified: obj.lastModified,
|
lastModified: obj.lastModified,
|
||||||
url: obj.url
|
url: obj.url,
|
||||||
}))
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,53 +4,54 @@
|
|||||||
|
|
||||||
/** STS 临时凭证 */
|
/** STS 临时凭证 */
|
||||||
export interface StsTokenData {
|
export interface StsTokenData {
|
||||||
region: string
|
region: string;
|
||||||
accessKeyId: string
|
accessKeyId: string;
|
||||||
accessKeySecret: string
|
accessKeySecret: string;
|
||||||
securityToken: string
|
securityToken: string;
|
||||||
bucket: string
|
bucket: string;
|
||||||
endpoint: string
|
endpoint: string;
|
||||||
uploadPrefix: string
|
uploadPrefix: string;
|
||||||
cdnDomain?: string
|
cdnDomain?: string;
|
||||||
expiration: string
|
expiration: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 角色提取结果 */
|
/** 角色提取结果 */
|
||||||
export interface CharacterItem {
|
export interface CharacterItem {
|
||||||
charId: string
|
charId: string;
|
||||||
name: string
|
name: string;
|
||||||
imageUrl: string
|
imageUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 作品详情 */
|
/** 作品详情 */
|
||||||
export interface WorkDetail {
|
export interface WorkDetail {
|
||||||
workId: string
|
workId: string;
|
||||||
orgId: string
|
orgId: string;
|
||||||
phone: string
|
phone: string;
|
||||||
status: number
|
status: number;
|
||||||
title: string
|
title: string;
|
||||||
author: string
|
author: string;
|
||||||
coverUrl: string
|
coverUrl: string;
|
||||||
pages: WorkPage[]
|
pages: WorkPage[];
|
||||||
style?: string
|
style?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 作品分页 */
|
/** 作品分页 */
|
||||||
export interface WorkPage {
|
export interface WorkPage {
|
||||||
pageNo: number
|
pageNo: number;
|
||||||
imageUrl: string
|
imageUrl: string;
|
||||||
text: string
|
text: string;
|
||||||
audioUrl?: string
|
audioUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 创建故事请求参数 */
|
/** 创建故事请求参数 */
|
||||||
export interface CreateStoryParams {
|
export interface CreateStoryParams {
|
||||||
imageUrl: string
|
imageUrl: string;
|
||||||
storyHint: string
|
storyHint: string;
|
||||||
style: string
|
style: string;
|
||||||
title?: string
|
heroName: string;
|
||||||
author?: string
|
title?: string;
|
||||||
heroCharId?: string
|
author?: string;
|
||||||
extractId?: string
|
heroCharId?: string;
|
||||||
refAdaptMode?: string
|
extractId?: string;
|
||||||
|
refAdaptMode?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -317,6 +317,7 @@ const startCreation = async () => {
|
|||||||
storyHint: store.storyData?.storyHint || '',
|
storyHint: store.storyData?.storyHint || '',
|
||||||
style: store.selectedStyle,
|
style: store.selectedStyle,
|
||||||
title: store.storyData?.title || '',
|
title: store.storyData?.title || '',
|
||||||
|
heroName: store.storyData?.heroName || '',
|
||||||
author: store.storyData?.author,
|
author: store.storyData?.author,
|
||||||
heroCharId: store.selectedCharacter?.charId,
|
heroCharId: store.selectedCharacter?.charId,
|
||||||
extractId: store.extractId,
|
extractId: store.extractId,
|
||||||
@ -330,14 +331,14 @@ const startCreation = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveWorkId(workId)
|
saveWorkId(workId)
|
||||||
progress.value = 10
|
progress.value = 0
|
||||||
stage.value = '故事构思中…'
|
stage.value = '故事构思中…'
|
||||||
// startWebSocket(workId)
|
// startWebSocket(workId)
|
||||||
startPolling(store.workId)
|
startPolling(store.workId)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error('e', e);
|
console.error('e', e);
|
||||||
if (store.workId) {
|
if (store.workId) {
|
||||||
progress.value = 10
|
progress.value = 0
|
||||||
stage.value = '创作已提交到后台…'
|
stage.value = '创作已提交到后台…'
|
||||||
startPolling(store.workId)
|
startPolling(store.workId)
|
||||||
} else {
|
} else {
|
||||||
@ -350,7 +351,7 @@ const startCreation = async () => {
|
|||||||
const resumePolling = () => {
|
const resumePolling = () => {
|
||||||
error.value = ''
|
error.value = ''
|
||||||
networkWarn.value = false
|
networkWarn.value = false
|
||||||
progress.value = 10
|
progress.value = 0
|
||||||
stage.value = '正在查询创作进度…'
|
stage.value = '正在查询创作进度…'
|
||||||
startPolling(store.workId)
|
startPolling(store.workId)
|
||||||
}
|
}
|
||||||
@ -391,7 +392,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
if (store.workId) {
|
if (store.workId) {
|
||||||
submitted = true
|
submitted = true
|
||||||
progress.value = 10
|
progress.value = 0
|
||||||
stage.value = '正在查询创作进度…'
|
stage.value = '正在查询创作进度…'
|
||||||
startPolling(store.workId)
|
startPolling(store.workId)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user