feat(aigc): 新增图片生成动效视频的升级功能
- 新增图片转视频相关的API接口 - 调整AIGC详情页英雄区块高度至380px - 添加结果详情页升级动效视频入口 - 完善升级流程的逻辑与费用计算 - 适配多类型生成任务的判断规则
This commit is contained in:
@@ -3,20 +3,22 @@
|
||||
<AigcTopBar :points="pointBalance" @back="handleBack" @history="handleOpenRecords" @recharge="handleRecharge" />
|
||||
|
||||
<view class="aigc-detail-content">
|
||||
<template v-if="taskDetail">
|
||||
<ResultPreview v-if="result.cover" class="aigc-detail-preview" :result="result" />
|
||||
<view class="aigc-detail-controls">
|
||||
<ResultMeta :items="metaItems" />
|
||||
<ResultActions :saving="isSaving" :media-type="result.mediaType" :share-ready="Boolean(shareKey)"
|
||||
:share-preparing="isPreparingShare" @save="handleSave" @prepare-share="handlePrepareShare" />
|
||||
|
||||
<view class="aigc-detail-text-actions">
|
||||
<view class="aigc-detail-text-action" @tap="handleRegenerate">再生成一版</view>
|
||||
<view class="aigc-detail-text-action" @tap="handleChangeTemplate">换个模板</view>
|
||||
<view class="aigc-detail-text-action" @tap="handleOpenRecords">历史记录</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template v-if="taskDetail">
|
||||
<ResultPreview v-if="result.cover" class="aigc-detail-preview" :result="result" />
|
||||
<view class="aigc-detail-controls">
|
||||
<!-- <ResultMeta :items="metaItems" /> -->
|
||||
<ContinuationCard v-if="result.mediaType === 'image'" :info="continuationInfo"
|
||||
@upgrade="handleUpgradeToVideo" />
|
||||
<ResultActions :saving="isSaving" :media-type="result.mediaType" :share-ready="Boolean(shareKey)"
|
||||
:share-preparing="isPreparingShare" @save="handleSave" @prepare-share="handlePrepareShare" />
|
||||
|
||||
<view class="aigc-detail-text-actions">
|
||||
<view class="aigc-detail-text-action" @tap="handleRegenerate">再生成一版</view>
|
||||
<view class="aigc-detail-text-action" @tap="handleChangeTemplate">换个模板</view>
|
||||
<view class="aigc-detail-text-action" @tap="handleOpenRecords">历史记录</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<Privacy :visible="privacyVisible" :contract-name="privacyContractName" @agree="handlePrivacyAgree"
|
||||
@@ -39,11 +41,11 @@ import {
|
||||
createAigcGeneratorTask,
|
||||
createAigcGeneratorTaskShare,
|
||||
getAigcGeneratorTaskDetail,
|
||||
} from "@/pages-aigc/request/AigcApi.js";
|
||||
} from "@/pages-aigc/request/AigcApi.js";
|
||||
import PointInsufficientDialog from "@/pages-aigc/useTemplate/components/PointInsufficientDialog/index.vue";
|
||||
import ContinuationCard from "./components/ContinuationCard/index.vue";
|
||||
import RegenerateConfirmPopup from "./components/RegenerateConfirmPopup/index.vue";
|
||||
import ResultActions from "./components/ResultActions/index.vue";
|
||||
import ResultMeta from "./components/ResultMeta/index.vue";
|
||||
import ResultPreview from "./components/ResultPreview/index.vue";
|
||||
|
||||
const { pointBalance, fetchCurrentCredit } = useCurrentCredit();
|
||||
@@ -57,6 +59,7 @@ const privacyContractName = ref("隐私保护指引");
|
||||
const pendingSaveAfterPrivacy = ref(false);
|
||||
const regeneratePopupRef = ref(null);
|
||||
const isRegenerating = ref(false);
|
||||
const isUpgradingToVideo = ref(false);
|
||||
const pointDialogVisible = ref(false);
|
||||
|
||||
const GENERATOR_TYPE_TEXT = {
|
||||
@@ -89,7 +92,7 @@ const result = computed(() => {
|
||||
|
||||
const imageUrl = record.imageResultUrl || "";
|
||||
const videoUrl = record.videoResultUrl || "";
|
||||
const isVideo = Number(record.generatorType) === 1;
|
||||
const isVideo = [1, 2].includes(Number(record.generatorType));
|
||||
const typeText = getMappedText(GENERATOR_TYPE_TEXT, record.generatorType, "类型");
|
||||
const statusText = getMappedText(TASK_STATUS_TEXT, record.taskStatus, "状态");
|
||||
const consumedText =
|
||||
@@ -138,6 +141,12 @@ const metaItems = computed(() => {
|
||||
|
||||
const sharePreviewImage = computed(() => taskDetail.value?.imageResultUrl || "");
|
||||
const regenerateCost = computed(() => Math.max(0, Number(taskDetail.value?.generatorCost) || 0));
|
||||
const continuationInfo = {
|
||||
title: "创意续作",
|
||||
desc: "让这张图动起来 · 5秒内动效视频",
|
||||
cost: "追加消耗 650积分",
|
||||
buttonText: "升级为动效视频",
|
||||
};
|
||||
|
||||
const showPlaceholderToast = (title) => {
|
||||
uni.showToast({
|
||||
@@ -182,6 +191,7 @@ onLoad((query = {}) => {
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
isUpgradingToVideo.value = false;
|
||||
fetchCurrentCredit();
|
||||
});
|
||||
|
||||
@@ -249,6 +259,35 @@ const handleRecharge = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpgradeToVideo = () => {
|
||||
if (isUpgradingToVideo.value || result.value.mediaType !== "image") return;
|
||||
|
||||
const sourceTaskId = String(taskDetail.value?.taskId || taskId.value || "").trim();
|
||||
if (!sourceTaskId) {
|
||||
showPlaceholderToast("缺少任务ID");
|
||||
return;
|
||||
}
|
||||
|
||||
const templateId = String(taskDetail.value?.templateId || "").trim();
|
||||
const templateItemId = String(taskDetail.value?.templateItemId || "").trim();
|
||||
const query = [
|
||||
templateId && `templateId=${encodeURIComponent(templateId)}`,
|
||||
`upgradeTaskId=${encodeURIComponent(sourceTaskId)}`,
|
||||
templateItemId && `upgradeTemplateItemId=${encodeURIComponent(templateItemId)}`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("&");
|
||||
|
||||
isUpgradingToVideo.value = true;
|
||||
uni.navigateTo({
|
||||
url: `/pages-aigc/useTemplate/useTemplate?${query}`,
|
||||
fail: () => {
|
||||
isUpgradingToVideo.value = false;
|
||||
showPlaceholderToast("打开图片生成页面失败");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getMediaFileExtension = (url, mediaType) => {
|
||||
const pathname = String(url || "").split(/[?#]/)[0];
|
||||
const matchedExtension = pathname.match(/\.([a-z0-9]{2,5})$/i)?.[1]?.toLowerCase();
|
||||
@@ -372,7 +411,7 @@ const removeDownloadedMedia = (filePath, shouldCleanup) => {
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.getFileSystemManager().unlink({
|
||||
filePath,
|
||||
fail: () => {},
|
||||
fail: () => { },
|
||||
});
|
||||
// #endif
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user