更新带货视频工作流接口和页面
This commit is contained in:
@@ -100,13 +100,35 @@ export interface UploadFileVo {
|
||||
localPath: string;
|
||||
size: number;
|
||||
relativePath?: string;
|
||||
objectKey?: string;
|
||||
url?: string;
|
||||
mediaType?: "image" | "video" | "audio" | "file" | string;
|
||||
}
|
||||
|
||||
export async function uploadTempFileToJava(file: File, relativePath?: string) {
|
||||
export interface UploadTempFileOptions {
|
||||
relativePath?: string;
|
||||
uploadToOss?: boolean;
|
||||
moduleType?: string;
|
||||
}
|
||||
|
||||
export async function uploadTempFileToJava(
|
||||
file: File,
|
||||
relativePathOrOptions?: string | UploadTempFileOptions,
|
||||
) {
|
||||
const options =
|
||||
typeof relativePathOrOptions === 'string'
|
||||
? { relativePath: relativePathOrOptions }
|
||||
: relativePathOrOptions || {}
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
if (relativePath) {
|
||||
formData.append('relativePath', relativePath)
|
||||
if (options.relativePath) {
|
||||
formData.append('relativePath', options.relativePath)
|
||||
}
|
||||
if (options.uploadToOss) {
|
||||
formData.append('uploadToOss', 'true')
|
||||
}
|
||||
if (options.moduleType) {
|
||||
formData.append('moduleType', options.moduleType)
|
||||
}
|
||||
const response = await http.post<JavaApiResponse<UploadFileVo>>(
|
||||
`${JAVA_API_PREFIX}/files/upload`,
|
||||
@@ -2437,6 +2459,11 @@ export interface SkipPriceAsinPageVo {
|
||||
totalPages: number;
|
||||
skipAsinsByCountry: Record<string, string[]>;
|
||||
skipAsinDetailsByCountry: Record<string, Array<{ asin?: string; minimumPrice?: string }>>;
|
||||
skip_asins?: Record<string, string[]>;
|
||||
skip_asins_by_country?: Record<string, string[]>;
|
||||
skip_asin_details_by_country?: Record<string, Array<{ asin?: string; minimumPrice?: string }>>;
|
||||
asin_rows_by_country?: Record<string, Array<{ asin?: string; minimumPrice?: string }>>;
|
||||
minimum_price_by_country_and_asin?: Record<string, Record<string, string>>;
|
||||
}
|
||||
|
||||
export function deletePendingPriceTrackShopResult(shopName: string) {
|
||||
@@ -2447,6 +2474,232 @@ export function deletePendingPriceTrackShopResult(shopName: string) {
|
||||
);
|
||||
}
|
||||
|
||||
// ========== 视频复刻 / 图生视频 ==========
|
||||
|
||||
export interface ImageVideoDouyinCopyVo {
|
||||
recognizedContent?: string;
|
||||
scriptDraft?: string;
|
||||
executeId?: string;
|
||||
debugUrl?: string;
|
||||
}
|
||||
|
||||
export interface ImageVideoSecretStatusVo {
|
||||
userId?: number;
|
||||
configured?: boolean;
|
||||
valid?: boolean;
|
||||
expired?: boolean;
|
||||
hasCopyApiKey?: boolean;
|
||||
hasT8Key?: boolean;
|
||||
hasVoiceApiKey?: boolean;
|
||||
hasVoiceGroupId?: boolean;
|
||||
copyApiKeyMasked?: string;
|
||||
t8KeyMasked?: string;
|
||||
voiceApiKeyMasked?: string;
|
||||
voiceGroupIdMasked?: string;
|
||||
expireDays?: number;
|
||||
expiresAt?: string;
|
||||
}
|
||||
|
||||
export interface ImageVideoSecretSavePayload {
|
||||
copyApiKey?: string;
|
||||
t8Key?: string;
|
||||
voiceApiKey?: string;
|
||||
voiceGroupId?: string;
|
||||
expireDays: number;
|
||||
}
|
||||
|
||||
export interface ImageVideoWorkflowParameters {
|
||||
api_key_info: {
|
||||
t8star_key: string;
|
||||
ai_conductor_key: string;
|
||||
};
|
||||
bg_info: {
|
||||
type: number;
|
||||
prompt: string;
|
||||
bg_image: string;
|
||||
};
|
||||
face_info: {
|
||||
type: number;
|
||||
model_figure: string;
|
||||
model_image: string[];
|
||||
};
|
||||
proc_info: {
|
||||
type: string;
|
||||
name: string;
|
||||
proc_image: string[];
|
||||
properties: string;
|
||||
};
|
||||
text_info: {
|
||||
type: number;
|
||||
language: string;
|
||||
text: string;
|
||||
file_url: string;
|
||||
};
|
||||
video_info: {
|
||||
video_url: string;
|
||||
share_url: string;
|
||||
ref_video_mode: string;
|
||||
mode: string;
|
||||
model: string;
|
||||
prompt: string;
|
||||
ratio: string;
|
||||
resolution: string;
|
||||
duration: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ImageVideoWorkflowResponse {
|
||||
code?: number;
|
||||
msg?: string;
|
||||
data?: string;
|
||||
execute_id?: string;
|
||||
debug_url?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ImageVideoMediaUploadVo {
|
||||
url: string;
|
||||
objectKey: string;
|
||||
originalFilename: string;
|
||||
mediaType: "image" | "video" | "audio" | string;
|
||||
}
|
||||
|
||||
export interface ImageVideoVoiceWorkflowResponse {
|
||||
code?: number;
|
||||
msg?: string;
|
||||
data?: unknown;
|
||||
execute_id?: string;
|
||||
debug_url?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ImageVideoWorkflowRunRequest {
|
||||
userId: number;
|
||||
parameters: ImageVideoWorkflowParameters;
|
||||
}
|
||||
|
||||
export interface ImageVideoWorkflowResultRequest {
|
||||
userId: number;
|
||||
executeId: string;
|
||||
}
|
||||
|
||||
export function getImageVideoSecretStatus() {
|
||||
return unwrapJavaResponse(
|
||||
get<JavaApiResponse<ImageVideoSecretStatusVo>>(
|
||||
`${JAVA_API_PREFIX}/image-video/secrets`,
|
||||
{ params: { user_id: getCurrentUserId() } },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function saveImageVideoSecrets(payload: ImageVideoSecretSavePayload) {
|
||||
return unwrapJavaResponse(
|
||||
put<JavaApiResponse<ImageVideoSecretStatusVo>, ImageVideoSecretSavePayload & { userId: number }>(
|
||||
`${JAVA_API_PREFIX}/image-video/secrets`,
|
||||
{ ...payload, userId: getCurrentUserId() },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function runImageVideoDouyinCopy(url: string) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<ImageVideoDouyinCopyVo>, { userId: number; url: string }>(
|
||||
`${JAVA_API_PREFIX}/image-video/douyin-copy`,
|
||||
{ userId: getCurrentUserId(), url },
|
||||
{ timeout: 180000 },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function runImageVideoWorkflow(parameters: ImageVideoWorkflowParameters) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<ImageVideoWorkflowResponse>, ImageVideoWorkflowRunRequest>(
|
||||
`${JAVA_API_PREFIX}/image-video/workflow/run`,
|
||||
{ userId: getCurrentUserId(), parameters },
|
||||
{ timeout: 120000 },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function getImageVideoWorkflowResult(executeId: string) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<ImageVideoWorkflowResponse>, ImageVideoWorkflowResultRequest>(
|
||||
`${JAVA_API_PREFIX}/image-video/workflow/result`,
|
||||
{ userId: getCurrentUserId(), executeId },
|
||||
{ timeout: 600000 },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export async function uploadImageVideoMedia(file: File) {
|
||||
const response = await uploadTempFileToJava(file, {
|
||||
uploadToOss: true,
|
||||
moduleType: "IMAGE_VIDEO",
|
||||
});
|
||||
if (!response.success) {
|
||||
throw new Error(response.message || "请求失败");
|
||||
}
|
||||
const data = response.data;
|
||||
if (!data?.url || !data.objectKey) {
|
||||
throw new Error("OSS 上传未返回有效 URL");
|
||||
}
|
||||
return {
|
||||
url: data.url,
|
||||
objectKey: data.objectKey,
|
||||
originalFilename: data.originalFilename || file.name,
|
||||
mediaType: data.mediaType || resolveImageVideoMediaType(file),
|
||||
} as ImageVideoMediaUploadVo;
|
||||
}
|
||||
|
||||
function resolveImageVideoMediaType(file: File) {
|
||||
const type = (file.type || "").toLowerCase();
|
||||
const name = (file.name || "").toLowerCase();
|
||||
if (type.startsWith("image/") || /\.(png|jpe?g|webp|gif|bmp|svg)$/.test(name)) return "image";
|
||||
if (type.startsWith("video/") || /\.(mp4|mov|webm|m4v|ogg|avi|mkv)$/.test(name)) return "video";
|
||||
if (type.startsWith("audio/") || /\.(mp3|wav|m4a|aac|flac)$/.test(name)) return "audio";
|
||||
return "file";
|
||||
}
|
||||
|
||||
export function listImageVideoVoices() {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<ImageVideoVoiceWorkflowResponse>, { userId: number }>(
|
||||
`${JAVA_API_PREFIX}/image-video/voice/list`,
|
||||
{ userId: getCurrentUserId() },
|
||||
{ timeout: 180000 },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteImageVideoVoice(voiceId: string) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<ImageVideoVoiceWorkflowResponse>, { userId: number; voiceId: string }>(
|
||||
`${JAVA_API_PREFIX}/image-video/voice/delete`,
|
||||
{ userId: getCurrentUserId(), voiceId },
|
||||
{ timeout: 180000 },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function cloneImageVideoVoice(payload: { audioUrl?: string; videoUrl?: string }) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<ImageVideoVoiceWorkflowResponse>, { userId: number; audioUrl?: string; videoUrl?: string }>(
|
||||
`${JAVA_API_PREFIX}/image-video/voice/clone`,
|
||||
{ userId: getCurrentUserId(), ...payload },
|
||||
{ timeout: 180000 },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function synthesizeImageVideoVoice(payload: { text: string; voiceId: string }) {
|
||||
return unwrapJavaResponse(
|
||||
post<JavaApiResponse<ImageVideoVoiceWorkflowResponse>, { userId: number; text: string; voiceId: string }>(
|
||||
`${JAVA_API_PREFIX}/image-video/voice/synthesis`,
|
||||
{ userId: getCurrentUserId(), ...payload },
|
||||
{ timeout: 180000 },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ========== 采集数据 ==========
|
||||
|
||||
export interface CollectDataSourceFile {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<div class="ai-asset-dropzone">
|
||||
<div
|
||||
class="ai-asset-dropzone"
|
||||
:class="{
|
||||
'ai-asset-dropzone--compact': compact,
|
||||
'ai-asset-dropzone--active': Boolean(fileName || sourceUrl),
|
||||
}"
|
||||
>
|
||||
<div class="ai-asset-dropzone__head">
|
||||
<div>
|
||||
<div class="ai-asset-dropzone__title">{{ title }}</div>
|
||||
@@ -21,16 +27,31 @@
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:accept="accept"
|
||||
:multiple="multiple"
|
||||
:disabled="loading"
|
||||
class="ai-asset-dropzone__upload"
|
||||
@change="handleChange"
|
||||
>
|
||||
<div v-if="previewUrl || (fileName && previewKind === 'file')" class="ai-asset-dropzone__preview">
|
||||
<img
|
||||
<div
|
||||
v-if="previewUrl || (fileName && previewKind === 'file')"
|
||||
class="ai-asset-dropzone__preview"
|
||||
:class="{
|
||||
'ai-asset-dropzone__preview--clickable': previewClickable && previewKind === 'image' && previewUrl,
|
||||
}"
|
||||
>
|
||||
<button
|
||||
v-if="previewKind === 'image'"
|
||||
:src="previewUrl"
|
||||
class="ai-asset-dropzone__media"
|
||||
alt=""
|
||||
/>
|
||||
type="button"
|
||||
class="ai-asset-dropzone__image-preview-btn"
|
||||
@click.stop.prevent="emit('preview', previewUrl)"
|
||||
>
|
||||
<img
|
||||
:src="previewUrl"
|
||||
class="ai-asset-dropzone__media"
|
||||
alt=""
|
||||
/>
|
||||
<span v-if="previewClickable" class="ai-asset-dropzone__preview-tip">点击放大预览</span>
|
||||
</button>
|
||||
<video
|
||||
v-else-if="previewKind === 'video'"
|
||||
:src="previewUrl"
|
||||
@@ -42,24 +63,28 @@
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ai-asset-dropzone__empty">
|
||||
<div class="ai-asset-dropzone__action">{{ loading ? '上传中...' : actionLabel }}</div>
|
||||
<div class="ai-asset-dropzone__placeholder">{{ placeholder }}</div>
|
||||
<div v-if="helper" class="ai-asset-dropzone__helper">{{ helper }}</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
|
||||
<div v-if="fileName" class="ai-asset-dropzone__footer">
|
||||
<span class="ai-asset-dropzone__selected-label">{{ selectedLabel }}</span>
|
||||
<span class="ai-asset-dropzone__filename">{{ fileName }}</span>
|
||||
</div>
|
||||
|
||||
<el-input
|
||||
v-if="allowUrl"
|
||||
:model-value="sourceUrl"
|
||||
class="ai-asset-dropzone__url"
|
||||
clearable
|
||||
:placeholder="urlPlaceholder"
|
||||
@change="(value) => $emit('url-change', value)"
|
||||
@clear="$emit('url-change', '')"
|
||||
/>
|
||||
<div v-if="allowUrl" class="ai-asset-dropzone__url-wrap">
|
||||
<span v-if="urlLabel" class="ai-asset-dropzone__url-label">{{ urlLabel }}</span>
|
||||
<el-input
|
||||
:model-value="sourceUrl"
|
||||
class="ai-asset-dropzone__url"
|
||||
clearable
|
||||
:placeholder="urlPlaceholder"
|
||||
@change="(value) => $emit('url-change', value)"
|
||||
@clear="$emit('url-change', '')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -79,6 +104,13 @@ withDefaults(
|
||||
allowUrl?: boolean
|
||||
sourceUrl?: string
|
||||
urlPlaceholder?: string
|
||||
urlLabel?: string
|
||||
actionLabel?: string
|
||||
selectedLabel?: string
|
||||
compact?: boolean
|
||||
loading?: boolean
|
||||
multiple?: boolean
|
||||
previewClickable?: boolean
|
||||
}>(),
|
||||
{
|
||||
description: '',
|
||||
@@ -91,6 +123,12 @@ withDefaults(
|
||||
allowUrl: false,
|
||||
sourceUrl: '',
|
||||
urlPlaceholder: '也可以粘贴视频在线链接',
|
||||
urlLabel: '',
|
||||
actionLabel: '点击或拖拽上传',
|
||||
selectedLabel: '已选择',
|
||||
compact: false,
|
||||
loading: false,
|
||||
previewClickable: false,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -98,6 +136,7 @@ const emit = defineEmits<{
|
||||
(event: 'select', file: File): void
|
||||
(event: 'clear'): void
|
||||
(event: 'url-change', value: string): void
|
||||
(event: 'preview', value: string): void
|
||||
}>()
|
||||
|
||||
function handleChange(uploadFile: UploadFile, _files: UploadFiles) {
|
||||
@@ -114,6 +153,7 @@ function handleChange(uploadFile: UploadFile, _files: UploadFiles) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__head {
|
||||
@@ -121,6 +161,11 @@ function handleChange(uploadFile: UploadFile, _files: UploadFiles) {
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__head > div {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__title {
|
||||
@@ -136,6 +181,10 @@ function handleChange(uploadFile: UploadFile, _files: UploadFiles) {
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__clear {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
:deep(.ai-asset-dropzone__upload .el-upload) {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -146,13 +195,19 @@ function handleChange(uploadFile: UploadFile, _files: UploadFiles) {
|
||||
padding: 12px;
|
||||
border: 1px dashed #474747;
|
||||
border-radius: 14px;
|
||||
background: #1f1f1f;
|
||||
transition: border-color 0.2s ease, background 0.2s ease;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.025), rgba(255, 255, 255, 0)),
|
||||
#1f1f1f;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
background 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.ai-asset-dropzone__upload .el-upload-dragger:hover) {
|
||||
border-color: #8c63ff;
|
||||
background: #232323;
|
||||
box-shadow: 0 0 0 1px rgba(140, 99, 255, 0.08) inset;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__preview,
|
||||
@@ -168,6 +223,22 @@ function handleChange(uploadFile: UploadFile, _files: UploadFiles) {
|
||||
.ai-asset-dropzone__empty {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__action {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 30px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid rgba(140, 99, 255, 0.38);
|
||||
border-radius: 999px;
|
||||
background: rgba(140, 99, 255, 0.12);
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__placeholder {
|
||||
@@ -188,6 +259,39 @@ function handleChange(uploadFile: UploadFile, _files: UploadFiles) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__image-preview-btn {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__preview--clickable .ai-asset-dropzone__image-preview-btn {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__preview-tip {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(0, 0, 0, 0.58);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.18s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__preview--clickable:hover .ai-asset-dropzone__preview-tip {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__file {
|
||||
color: #d8d8d8;
|
||||
font-size: 13px;
|
||||
@@ -195,18 +299,42 @@ function handleChange(uploadFile: UploadFile, _files: UploadFiles) {
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
background: #1f1f1f;
|
||||
border: 1px solid #383838;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__selected-label {
|
||||
flex-shrink: 0;
|
||||
color: #8d8d8d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__filename {
|
||||
min-width: 0;
|
||||
color: #d8d8d8;
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__url-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__url-label {
|
||||
color: #9d9d9d;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone__url {
|
||||
--el-input-bg-color: #171717;
|
||||
--el-input-border-color: #383838;
|
||||
@@ -215,4 +343,22 @@ function handleChange(uploadFile: UploadFile, _files: UploadFiles) {
|
||||
--el-input-text-color: #f2f2f2;
|
||||
--el-input-placeholder-color: #777;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone--compact {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone--compact :deep(.ai-asset-dropzone__upload .el-upload-dragger) {
|
||||
min-height: 138px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone--compact .ai-asset-dropzone__preview,
|
||||
.ai-asset-dropzone--compact .ai-asset-dropzone__empty {
|
||||
min-height: 118px;
|
||||
}
|
||||
|
||||
.ai-asset-dropzone--compact .ai-asset-dropzone__media {
|
||||
height: 118px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user