diff --git a/src/pages-aigc/detail/detail.vue b/src/pages-aigc/detail/detail.vue index d0e5f40..90bc368 100644 --- a/src/pages-aigc/detail/detail.vue +++ b/src/pages-aigc/detail/detail.vue @@ -3,20 +3,22 @@ - + { 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 }; diff --git a/src/pages-aigc/request/AigcApi.js b/src/pages-aigc/request/AigcApi.js index d8e1b1c..ab6b04e 100644 --- a/src/pages-aigc/request/AigcApi.js +++ b/src/pages-aigc/request/AigcApi.js @@ -30,6 +30,21 @@ function createAigcGeneratorTask(args) { ); } +function createAigcImageToVideoTask(args) { + return request.post( + "/hotelBiz/aigcGeneratorTask/createAigcImageToVideoTask", + args, + { sensitive: true }, + ); +} + +function getAigcImageToVideoItemDetail(args) { + return request.post( + "/hotelBiz/aigcGeneratorTask/aigcImageToVideoItemDetail", + args, + ); +} + function getCurrentCredit() { return request.get("/hotelBiz/credit/current", {}); } @@ -62,6 +77,8 @@ export { getAigcGeneratorTaskList, getAigcGeneratorTaskDetail, createAigcGeneratorTask, + createAigcImageToVideoTask, + getAigcImageToVideoItemDetail, getCurrentCredit, getCreditLedgerPage, getPointsTopUpAgreement, diff --git a/src/pages-aigc/useTemplate/components/TemplateVersionHero/styles/index.scss b/src/pages-aigc/useTemplate/components/TemplateVersionHero/styles/index.scss index 4b44cff..b9d4ba7 100644 --- a/src/pages-aigc/useTemplate/components/TemplateVersionHero/styles/index.scss +++ b/src/pages-aigc/useTemplate/components/TemplateVersionHero/styles/index.scss @@ -1,6 +1,6 @@ .template-version-hero { position: relative; - height: 360px; + height: 380px; overflow: hidden; border-radius: 24px; background: var(--template-accent, #0a4d46); diff --git a/src/pages-aigc/useTemplate/useTemplate.vue b/src/pages-aigc/useTemplate/useTemplate.vue index c7519c3..17f3c96 100644 --- a/src/pages-aigc/useTemplate/useTemplate.vue +++ b/src/pages-aigc/useTemplate/useTemplate.vue @@ -46,11 +46,14 @@ import { onLoad, onShow } from "@dcloudio/uni-app"; import Privacy from "@/components/Privacy/index.vue"; import AigcTopBar from "@/pages-aigc/components/AigcTopBar/index.vue"; import { useCurrentCredit } from "@/pages-aigc/hooks/useCurrentCredit.js"; -import { getWechatLoginCode } from "@/pages-aigc/services/wechatLogin.js"; -import { - createAigcGeneratorTask, - getAigcTemplateItemList, - getAigcTemplateList, +import { getWechatLoginCode } from "@/pages-aigc/services/wechatLogin.js"; +import { + createAigcImageToVideoTask, + createAigcGeneratorTask, + getAigcImageToVideoItemDetail, + getAigcGeneratorTaskDetail, + getAigcTemplateItemList, + getAigcTemplateList, } from "@/pages-aigc/request/AigcApi.js"; import { updateImageFile } from "@/request/api/UpdateFile.js"; import GenerateConfirmPanel from "./components/GenerateConfirmPanel/index.vue"; @@ -72,14 +75,27 @@ const uploadedImageUrl = ref(""); const currentStep = ref("version"); const pointDialogVisible = ref(false); const isUploading = ref(false); -const isCreatingTask = ref(false); -const privacyVisible = ref(false); -const privacyContractName = ref("隐私保护指引"); -const pendingImageSourceType = ref(""); +const isCreatingTask = ref(false); +const privacyVisible = ref(false); +const privacyContractName = ref("隐私保护指引"); +const pendingImageSourceType = ref(""); +const upgradeTaskId = ref(""); +const upgradeTemplateItemId = ref(""); +const imageToVideoItemDetail = ref(null); +const upgradeSourceTaskDetail = ref(null); + +const isImageToVideoUpgrade = computed(() => Boolean(upgradeTaskId.value)); +const IMAGE_GENERATION_COST = 350; +const VIDEO_GENERATION_COST = 1000; -const currentTemplate = computed(() => { - return templates.value.find((item) => item.templateId === templateId.value) || templates.value[0] || {}; -}); +const currentTemplate = computed(() => { + const template = + templates.value.find((item) => item.templateId === templateId.value) || templates.value[0] || {}; + const imageResultUrl = String(upgradeSourceTaskDetail.value?.imageResultUrl || "").trim(); + + if (!isImageToVideoUpgrade.value || !imageResultUrl) return template; + return { ...template, templateContentUrl: imageResultUrl }; +}); const currentTemplateItem = computed(() => { return ( @@ -89,16 +105,54 @@ const currentTemplateItem = computed(() => { ); }); -const currentCost = computed(() => { - return Number(currentTemplateItem.value.generatorCost) || 0; -}); +const currentCost = computed(() => { + return isImageToVideoUpgrade.value + ? Number(imageToVideoItemDetail.value?.generatorCost) || 0 + : Number(currentTemplateItem.value.generatorType) === 1 + ? VIDEO_GENERATION_COST + : IMAGE_GENERATION_COST; +}); -const showPlaceholderToast = (title) => { +const showPlaceholderToast = (title) => { uni.showToast({ title, icon: "none", - }); -}; + }); +}; + +const fetchImageToVideoItemDetail = async () => { + if (!isImageToVideoUpgrade.value) return; + + try { + const res = await getAigcImageToVideoItemDetail({ taskId: upgradeTaskId.value }); + if (res?.code === 0 && res.data && typeof res.data === "object" && !Array.isArray(res.data)) { + imageToVideoItemDetail.value = res.data; + return; + } + + showPlaceholderToast(res?.msg || "获取升级视频费用失败"); + } catch (error) { + console.warn("获取AIGC图片升级视频生成项详情失败", error); + showPlaceholderToast("获取升级视频费用失败"); + } +}; + +const fetchUpgradeSourceTaskDetail = async () => { + if (!isImageToVideoUpgrade.value) return; + + try { + const res = await getAigcGeneratorTaskDetail({ taskId: upgradeTaskId.value }); + if (res?.code === 0 && res.data && typeof res.data === "object" && !Array.isArray(res.data)) { + upgradeSourceTaskDetail.value = res.data; + return; + } + + showPlaceholderToast("获取原图片失败"); + } catch (error) { + console.warn("获取AIGC原图片任务详情失败", error); + showPlaceholderToast("获取原图片失败"); + } +}; const resetSelectedPhoto = () => { selectedImageLocalPath.value = ""; @@ -127,25 +181,39 @@ const fetchTemplateItemList = async () => { } try { - const res = await getAigcTemplateItemList({ templateId: templateId.value }); - if (res?.code === 0 && Array.isArray(res.data)) { - templateItems.value = res.data; - selectedTemplateItemId.value = res.data[0]?.templateItemId || ""; - } + const res = await getAigcTemplateItemList({ templateId: templateId.value }); + if (res?.code === 0 && Array.isArray(res.data)) { + templateItems.value = res.data; + const selectedUpgradeItem = res.data.find( + (item) => + String(item.templateItemId || "") === upgradeTemplateItemId.value && + Number(item.generatorType) === 0, + ); + const firstImageItem = res.data.find((item) => Number(item.generatorType) === 0); + selectedTemplateItemId.value = + (isImageToVideoUpgrade.value ? selectedUpgradeItem || firstImageItem : res.data[0])?.templateItemId || ""; + if (isImageToVideoUpgrade.value) { + currentStep.value = "generateConfirm"; + } + } } catch (error) { console.warn("获取AIGC模板生成项失败", error); } }; -const fetchPageData = async () => { - await fetchTemplateList(); - await fetchTemplateItemList(); -}; +const fetchPageData = async () => { + await fetchTemplateList(); + await fetchImageToVideoItemDetail(); + await fetchUpgradeSourceTaskDetail(); + await fetchTemplateItemList(); +}; -onLoad((query = {}) => { - templateId.value = query.templateId || ""; - fetchPageData(); -}); +onLoad((query = {}) => { + templateId.value = String(query.templateId || "").trim(); + upgradeTaskId.value = String(query.upgradeTaskId || "").trim(); + upgradeTemplateItemId.value = String(query.upgradeTemplateItemId || "").trim(); + fetchPageData(); +}); onShow(() => { fetchCurrentCredit(); @@ -317,26 +385,31 @@ const handleClosePhotoConfirm = () => { currentStep.value = "photoPick"; }; -const handleConfirmPhoto = () => { - if (!uploadedImageUrl.value) { - showPlaceholderToast("请先上传图片"); - currentStep.value = "photoPick"; +const handleConfirmPhoto = () => { + if (!uploadedImageUrl.value) { + showPlaceholderToast("请先上传图片"); + currentStep.value = "photoPick"; return; } currentStep.value = "generateConfirm"; }; -const handleGenerate = async () => { - if (isCreatingTask.value) return; +const handleGenerate = async () => { + if (isCreatingTask.value) return; const templateItemId = currentTemplateItem.value.templateItemId; - if (!templateItemId) { - showPlaceholderToast("请选择生成类型"); - return; - } + if (!templateItemId) { + showPlaceholderToast("请选择生成类型"); + return; + } + + if (isImageToVideoUpgrade.value && !imageToVideoItemDetail.value) { + showPlaceholderToast("升级视频费用加载失败,请稍后重试"); + return; + } - if (!uploadedImageUrl.value) { + if (!isImageToVideoUpgrade.value && !uploadedImageUrl.value) { showPlaceholderToast("请先上传图片"); currentStep.value = "photoPick"; return; @@ -354,17 +427,33 @@ const handleGenerate = async () => { uni.showLoading({ title: "创建中", mask: true, - }); - - try { - const wxLoginCode = await getWechatLoginCode(); - const res = await createAigcGeneratorTask({ - templateItemId, - imageUrlList: [uploadedImageUrl.value], - wxLoginCode, - }); - - const taskId = String(res?.data || "").trim(); + }); + + try { + let res; + if (isImageToVideoUpgrade.value) { + const wxLoginCode = await getWechatLoginCode(); + res = await createAigcImageToVideoTask({ + taskId: upgradeTaskId.value, + wxLoginCode, + }); + } else { + const wxLoginCode = await getWechatLoginCode(); + res = await createAigcGeneratorTask({ + templateItemId, + imageUrlList: [uploadedImageUrl.value], + wxLoginCode, + }); + } + + if (isImageToVideoUpgrade.value && String(res?.msg || "").includes("积分余额不足")) { + pointDialogVisible.value = true; + return; + } + + const taskId = String( + typeof res?.data === "object" && res.data !== null ? res.data.taskId : res?.data || "", + ).trim(); if (res?.code === 0 && taskId) { fetchCurrentCredit(); uni.redirectTo({