feat(aigc/useTemplate): add multi-step template generation confirmation workflow
- add PhotoConfirmDrawer component for post-photo-pick quality verification - add GenerateConfirmPanel to display generation point cost and available balance - extend TemplateVersionHero to support custom variant styles - refactor useTemplate page to use step-based workflow flow
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<view class="generate-confirm-panel">
|
||||||
|
<view class="generate-points-summary">
|
||||||
|
<view class="generate-point-card">
|
||||||
|
<text class="generate-point-label">本次消耗</text>
|
||||||
|
<text class="generate-point-value">{{ cost }}积分</text>
|
||||||
|
</view>
|
||||||
|
<view class="generate-point-card">
|
||||||
|
<text class="generate-point-label">当前可用</text>
|
||||||
|
<text class="generate-point-value">{{ balance }}积分</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="generate-freeze-note">
|
||||||
|
<text class="generate-freeze-title">积分冻结规则</text>
|
||||||
|
<text class="generate-freeze-desc">
|
||||||
|
开始生成后将先冻结 {{ cost }} 积分,生成成功后扣除,失败会自动退回。
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="generate-confirm-actions">
|
||||||
|
<view
|
||||||
|
class="generate-action is-primary"
|
||||||
|
hover-class="is-pressed"
|
||||||
|
@tap="emit('generate')"
|
||||||
|
>
|
||||||
|
开始生成
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="generate-action is-secondary"
|
||||||
|
hover-class="is-pressed"
|
||||||
|
@tap="emit('recharge')"
|
||||||
|
>
|
||||||
|
充值积分
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
cost: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 100,
|
||||||
|
},
|
||||||
|
balance: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 300,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["generate", "recharge"]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "./styles/index.scss";
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
.generate-confirm-panel {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-points-summary {
|
||||||
|
height: 80px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-point-card {
|
||||||
|
min-width: 0;
|
||||||
|
height: 80px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 18px;
|
||||||
|
background: rgba(255, 255, 255, 0.68);
|
||||||
|
box-shadow: 0 8px 18px rgba(18, 48, 56, 0.04);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-point-label,
|
||||||
|
.generate-point-value {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-point-label {
|
||||||
|
color: #7a8596;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-point-value {
|
||||||
|
margin-top: 7px;
|
||||||
|
color: #102942;
|
||||||
|
font-size: 21px;
|
||||||
|
line-height: 27px;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-freeze-note {
|
||||||
|
min-height: 86px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 14px;
|
||||||
|
padding: 17px 16px;
|
||||||
|
border-radius: 18px;
|
||||||
|
background: rgba(255, 255, 255, 0.72);
|
||||||
|
box-shadow: 0 8px 18px rgba(18, 48, 56, 0.04);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-freeze-title,
|
||||||
|
.generate-freeze-desc {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-freeze-title {
|
||||||
|
color: #172033;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 900;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-freeze-desc {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: #52637a;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-confirm-actions {
|
||||||
|
height: 48px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-action {
|
||||||
|
min-width: 0;
|
||||||
|
height: 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: 900;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-action.is-primary {
|
||||||
|
background: #20cf75;
|
||||||
|
color: #ffffff;
|
||||||
|
box-shadow: 0 12px 24px rgba(32, 207, 117, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-action.is-secondary {
|
||||||
|
background: rgba(255, 255, 255, 0.82);
|
||||||
|
color: #3d5066;
|
||||||
|
}
|
||||||
|
|
||||||
|
.generate-action.is-pressed {
|
||||||
|
opacity: 0.86;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<uni-popup
|
||||||
|
ref="popupRef"
|
||||||
|
type="bottom"
|
||||||
|
background-color="transparent"
|
||||||
|
mask-background-color="rgba(0, 0, 0, 0)"
|
||||||
|
:safe-area="false"
|
||||||
|
:is-mask-click="false"
|
||||||
|
>
|
||||||
|
<view class="photo-confirm-drawer">
|
||||||
|
<view class="photo-confirm-close" @tap="emit('close')">
|
||||||
|
<uni-icons type="closeempty" size="30" color="#8fa2ba" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<text class="photo-confirm-title">照片确认</text>
|
||||||
|
|
||||||
|
<view class="photo-confirm-card">
|
||||||
|
<view class="photo-confirm-avatar">
|
||||||
|
<image class="photo-confirm-avatar-image" :src="avatarSrc" mode="aspectFill" />
|
||||||
|
</view>
|
||||||
|
<view class="photo-confirm-copy">
|
||||||
|
<text class="photo-confirm-card-title">照片已通过基础质检</text>
|
||||||
|
<text class="photo-confirm-card-desc">正脸清晰 · 单人入镜 · 光线可用</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="photo-confirm-primary" hover-class="is-pressed" @tap="emit('confirm')">
|
||||||
|
下一步确认
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { nextTick, onMounted, ref } from "vue";
|
||||||
|
import defaultAvatar from "../../assets/xiaoqi-avatar.png";
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
avatarSrc: {
|
||||||
|
type: String,
|
||||||
|
default: defaultAvatar,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["close", "confirm"]);
|
||||||
|
const popupRef = ref(null);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
popupRef.value?.open();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "./styles/index.scss";
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
.photo-confirm-drawer {
|
||||||
|
position: relative;
|
||||||
|
width: 100vw;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 446px;
|
||||||
|
margin-top: -34px;
|
||||||
|
padding: 30px 38px 28px;
|
||||||
|
border-radius: 28px 28px 0 0;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 -8px 28px rgba(18, 35, 48, 0.08);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-close {
|
||||||
|
position: absolute;
|
||||||
|
left: 24px;
|
||||||
|
top: 28px;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #9aa4b2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-title {
|
||||||
|
display: block;
|
||||||
|
margin: 6px 0 23px;
|
||||||
|
color: #111827;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-weight: 900;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-card {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 74px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 13px 16px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: #f0fbfd;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-avatar {
|
||||||
|
flex: 0 0 48px;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #d8f1f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-avatar-image {
|
||||||
|
width: 64px;
|
||||||
|
height: 92px;
|
||||||
|
display: block;
|
||||||
|
margin: -14px 0 0 -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-copy {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-card-title,
|
||||||
|
.photo-confirm-card-desc {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-card-title {
|
||||||
|
color: #172033;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 19px;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-card-desc {
|
||||||
|
margin-top: 2px;
|
||||||
|
color: #56708c;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-primary {
|
||||||
|
width: 100%;
|
||||||
|
height: 56px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 26px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #061e34;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: 900;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-confirm-primary.is-pressed {
|
||||||
|
opacity: 0.86;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<view
|
<view
|
||||||
class="template-version-hero"
|
class="template-version-hero"
|
||||||
|
:class="`is-${variant}`"
|
||||||
:style="{ '--template-accent': template.accent || '#0a4d46' }"
|
:style="{ '--template-accent': template.accent || '#0a4d46' }"
|
||||||
>
|
>
|
||||||
<image class="template-version-image" :src="template.cover" mode="aspectFill" />
|
<image class="template-version-image" :src="template.cover" mode="aspectFill" />
|
||||||
@@ -20,6 +21,10 @@ defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
variant: {
|
||||||
|
type: String,
|
||||||
|
default: "default",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,12 @@
|
|||||||
box-shadow: 0 16px 30px rgba(15, 29, 37, 0.1);
|
box-shadow: 0 16px 30px rgba(15, 29, 37, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.template-version-hero.is-confirm {
|
||||||
|
height: 248px;
|
||||||
|
border-radius: 18px;
|
||||||
|
box-shadow: 0 12px 24px rgba(15, 29, 37, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
.template-version-image {
|
.template-version-image {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -82,3 +88,28 @@
|
|||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.template-version-hero.is-confirm .template-version-copy {
|
||||||
|
right: 16px;
|
||||||
|
bottom: 18px;
|
||||||
|
left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-version-hero.is-confirm .template-version-badge {
|
||||||
|
height: 22px;
|
||||||
|
max-width: 88px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-version-hero.is-confirm .template-version-title {
|
||||||
|
margin-top: 9px;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.template-version-hero.is-confirm .template-version-desc {
|
||||||
|
max-width: 294px;
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 24px 18px;
|
padding: 18px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,15 +12,25 @@
|
|||||||
<view class="aigc-use-template-content">
|
<view class="aigc-use-template-content">
|
||||||
<TemplateVersionHero :template="currentTemplate" />
|
<TemplateVersionHero :template="currentTemplate" />
|
||||||
|
|
||||||
<VersionOptionList
|
<GenerateConfirmPanel
|
||||||
:options="versionOptions"
|
v-if="currentStep === 'generateConfirm'"
|
||||||
:selected-value="selectedVersion"
|
:cost="currentVersion.cost || 100"
|
||||||
@select="handleSelectVersion"
|
:balance="pointBalance"
|
||||||
|
@generate="handleGenerate"
|
||||||
|
@recharge="handleRecharge"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<text class="aigc-use-template-note">
|
<template v-else>
|
||||||
动效视频版会先生成图片底图,再升级为 5秒内动效视频。
|
<VersionOptionList
|
||||||
</text>
|
:options="versionOptions"
|
||||||
|
:selected-value="selectedVersion"
|
||||||
|
@select="handleSelectVersion"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<text class="aigc-use-template-note">
|
||||||
|
动效视频版会先生成图片底图,再升级为 5秒内动效视频。
|
||||||
|
</text>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -40,6 +50,13 @@
|
|||||||
@album="handlePickAlbum"
|
@album="handlePickAlbum"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-if="currentStep === 'photoConfirm'" class="aigc-use-template-popup-host">
|
||||||
|
<PhotoConfirmDrawer
|
||||||
|
:avatar-src="guideAvatar"
|
||||||
|
@close="handleClosePhotoConfirm"
|
||||||
|
@confirm="handleConfirmPhoto"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -49,6 +66,8 @@ import { onLoad } from "@dcloudio/uni-app";
|
|||||||
import AigcTopBar from "@/pages-aigc/components/AigcTopBar/index.vue";
|
import AigcTopBar from "@/pages-aigc/components/AigcTopBar/index.vue";
|
||||||
import { aigcTemplates } from "@/pages-aigc/home/data/templates.js";
|
import { aigcTemplates } from "@/pages-aigc/home/data/templates.js";
|
||||||
import guideAvatar from "./assets/xiaoqi-avatar.png";
|
import guideAvatar from "./assets/xiaoqi-avatar.png";
|
||||||
|
import GenerateConfirmPanel from "./components/GenerateConfirmPanel/index.vue";
|
||||||
|
import PhotoConfirmDrawer from "./components/PhotoConfirmDrawer/index.vue";
|
||||||
import PhotoGuidePanel from "./components/PhotoGuidePanel/index.vue";
|
import PhotoGuidePanel from "./components/PhotoGuidePanel/index.vue";
|
||||||
import PhotoPickDrawer from "./components/PhotoPickDrawer/index.vue";
|
import PhotoPickDrawer from "./components/PhotoPickDrawer/index.vue";
|
||||||
import TemplateVersionHero from "./components/TemplateVersionHero/index.vue";
|
import TemplateVersionHero from "./components/TemplateVersionHero/index.vue";
|
||||||
@@ -56,7 +75,7 @@ import VersionOptionList from "./components/VersionOptionList/index.vue";
|
|||||||
import { negativeExamples, positiveExamples } from "./data/photoGuideExamples.js";
|
import { negativeExamples, positiveExamples } from "./data/photoGuideExamples.js";
|
||||||
import { versionOptions } from "./data/versionOptions.js";
|
import { versionOptions } from "./data/versionOptions.js";
|
||||||
|
|
||||||
const pointBalance = ref(0);
|
const pointBalance = ref(300);
|
||||||
const templateId = ref("");
|
const templateId = ref("");
|
||||||
const selectedVersion = ref("image");
|
const selectedVersion = ref("image");
|
||||||
const currentStep = ref("version");
|
const currentStep = ref("version");
|
||||||
@@ -65,6 +84,10 @@ const currentTemplate = computed(() => {
|
|||||||
return aigcTemplates.find((item) => item.id === templateId.value) || aigcTemplates[0] || {};
|
return aigcTemplates.find((item) => item.id === templateId.value) || aigcTemplates[0] || {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const currentVersion = computed(() => {
|
||||||
|
return versionOptions.find((item) => item.value === selectedVersion.value) || versionOptions[0] || {};
|
||||||
|
});
|
||||||
|
|
||||||
onLoad((query = {}) => {
|
onLoad((query = {}) => {
|
||||||
templateId.value = query.templateId || "";
|
templateId.value = query.templateId || "";
|
||||||
});
|
});
|
||||||
@@ -111,11 +134,23 @@ const handleConfirmGuide = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handlePickCamera = () => {
|
const handlePickCamera = () => {
|
||||||
showPlaceholderToast("拍照");
|
currentStep.value = "photoConfirm";
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePickAlbum = () => {
|
const handlePickAlbum = () => {
|
||||||
showPlaceholderToast("相册");
|
currentStep.value = "photoConfirm";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClosePhotoConfirm = () => {
|
||||||
|
currentStep.value = "photoPick";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirmPhoto = () => {
|
||||||
|
currentStep.value = "generateConfirm";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGenerate = () => {
|
||||||
|
showPlaceholderToast("开始生成");
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user