forked from natuka/web.puabadge.com
518 lines
11 KiB
Vue
518 lines
11 KiB
Vue
<template>
|
||
<div class="success-page">
|
||
<DyNavBar
|
||
theme="light"
|
||
title="提交成功"
|
||
right-text="生图记录"
|
||
border
|
||
@back="goHome"
|
||
@right="goRecords"
|
||
/>
|
||
|
||
<div class="body">
|
||
<div class="hero">
|
||
<div class="hero-rings" aria-hidden="true">
|
||
<span class="ring r1" />
|
||
<span class="ring r2" />
|
||
<span class="ring r3" />
|
||
</div>
|
||
<div class="hero-icon">
|
||
<span class="check">✓</span>
|
||
</div>
|
||
<h1 class="hero-title">提交制作成功</h1>
|
||
<p class="hero-desc">{{ heroDesc }}</p>
|
||
</div>
|
||
|
||
<div v-if="resultList.length === 1" class="thumb-card" @click="openZoom(0)">
|
||
<img :src="resultList[0].url" class="thumb-img" alt="已提交效果图">
|
||
<div class="thumb-badge">已提交</div>
|
||
<div v-if="resultList[0].count > 1" class="thumb-count">×{{ resultList[0].count }}</div>
|
||
</div>
|
||
|
||
<div v-else-if="resultList.length > 1" class="thumb-grid">
|
||
<div
|
||
v-for="(item, idx) in resultList"
|
||
:key="`${item.taskId || item.url}-${idx}`"
|
||
class="thumb-grid-item"
|
||
@click="openZoom(idx)"
|
||
>
|
||
<img :src="item.url" class="thumb-img" alt="已提交效果图">
|
||
<div class="thumb-badge">已提交</div>
|
||
<div v-if="item.count > 1" class="thumb-count">×{{ item.count }}</div>
|
||
<div v-if="item.label" class="thumb-label">{{ item.label }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="steps-card">
|
||
<div class="steps-title">接下来会怎样</div>
|
||
<ul class="steps">
|
||
<li class="step is-done">
|
||
<span class="dot" />
|
||
<div>
|
||
<div class="step-name">确认效果并提交</div>
|
||
<div class="step-desc">已完成{{ totalCountText }}</div>
|
||
</div>
|
||
</li>
|
||
<li class="step is-active">
|
||
<span class="dot" />
|
||
<div>
|
||
<div class="step-name">进入制作流程</div>
|
||
<div class="step-desc">制作团队将按效果图开始生产</div>
|
||
</div>
|
||
</li>
|
||
<li class="step">
|
||
<span class="dot" />
|
||
<div>
|
||
<div class="step-name">完成后通知发货</div>
|
||
<div class="step-desc">请留意订单物流与消息通知</div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="tips-card">
|
||
<div class="tips-title">温馨提示</div>
|
||
<p>本订单提交后不可再次上传生图,如需查看效果请前往生图记录。</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="footer">
|
||
<button class="btn-ghost" type="button" @click="goHome">返回首页</button>
|
||
<button class="btn-primary" type="button" @click="goRecords">查看生成记录</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, onMounted, ref } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { showImagePreview } from 'vant'
|
||
import { douyinConfig as config } from '@/config/douyin'
|
||
import DyNavBar from './components/DyNavBar.vue'
|
||
|
||
type SuccessResult = {
|
||
url: string
|
||
count: number
|
||
label: string
|
||
taskId?: string
|
||
imageId?: string
|
||
}
|
||
|
||
const router = useRouter()
|
||
const resultList = ref<SuccessResult[]>([])
|
||
|
||
function normalizeResult(item: any): SuccessResult | null {
|
||
const url = String(item?.url || item?.path || item?.result_url || '').trim()
|
||
if (!url) return null
|
||
return {
|
||
url,
|
||
count: Math.max(1, Number(item?.count) || 1),
|
||
label: String(item?.label || item?.style || '').trim(),
|
||
taskId: item?.taskId ? String(item.taskId) : '',
|
||
imageId: item?.imageId ? String(item.imageId) : '',
|
||
}
|
||
}
|
||
|
||
function loadResults() {
|
||
const list: SuccessResult[] = []
|
||
try {
|
||
const raw = sessionStorage.getItem('douyin_success_results')
|
||
if (raw) {
|
||
const parsed = JSON.parse(raw)
|
||
if (Array.isArray(parsed)) {
|
||
parsed.forEach((item) => {
|
||
const normalized = normalizeResult(item)
|
||
if (normalized) list.push(normalized)
|
||
})
|
||
}
|
||
}
|
||
} catch (_) {}
|
||
|
||
if (!list.length) {
|
||
const single = sessionStorage.getItem('douyin_result_url') || ''
|
||
if (single) {
|
||
list.push({
|
||
url: single,
|
||
count: 1,
|
||
label: '',
|
||
})
|
||
}
|
||
}
|
||
|
||
// 去重,保留首次出现顺序
|
||
const seen = new Set<string>()
|
||
resultList.value = list.filter((item) => {
|
||
const key = `${item.taskId || ''}|${item.imageId || ''}|${item.url}`
|
||
if (seen.has(key)) return false
|
||
seen.add(key)
|
||
return true
|
||
})
|
||
}
|
||
|
||
const totalPieces = computed(() =>
|
||
resultList.value.reduce((sum, item) => sum + (Number(item.count) || 1), 0),
|
||
)
|
||
|
||
const heroDesc = computed(() => {
|
||
const n = resultList.value.length
|
||
if (n > 1) {
|
||
return `已提交 ${n} 张效果图(共 ${totalPieces.value} 件),可随时在生图记录中查看`
|
||
}
|
||
if (totalPieces.value > 1) {
|
||
return `已提交 ${totalPieces.value} 件制作,可随时在生图记录中查看`
|
||
}
|
||
return '效果图已确认并进入制作流程,可随时在生图记录中查看'
|
||
})
|
||
|
||
const totalCountText = computed(() => {
|
||
const n = resultList.value.length
|
||
if (n > 1) return ` · ${n} 张 / ${totalPieces.value} 件`
|
||
if (totalPieces.value > 1) return ` · ${totalPieces.value} 件`
|
||
return ''
|
||
})
|
||
|
||
function goHome() {
|
||
router.replace(config.routes.home)
|
||
}
|
||
|
||
function goRecords() {
|
||
router.replace(config.routes.records)
|
||
}
|
||
|
||
function openZoom(startIndex = 0) {
|
||
const images = resultList.value.map((item) => item.url).filter(Boolean)
|
||
if (!images.length) return
|
||
showImagePreview({
|
||
images,
|
||
startPosition: Math.max(0, Math.min(startIndex, images.length - 1)),
|
||
closeable: true,
|
||
closeIconPosition: 'top-right',
|
||
})
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadResults()
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.success-page {
|
||
min-height: 100vh;
|
||
min-height: 100dvh;
|
||
background: #f7faf6;
|
||
box-sizing: border-box;
|
||
padding-bottom: calc(88px + env(safe-area-inset-bottom));
|
||
}
|
||
|
||
.body {
|
||
padding: 8px 16px 24px;
|
||
animation: pageIn 0.55s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
@keyframes pageIn {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(10px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.hero {
|
||
position: relative;
|
||
text-align: center;
|
||
padding: 28px 12px 20px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.hero-rings {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 42px;
|
||
width: 120px;
|
||
height: 120px;
|
||
transform: translateX(-50%);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.ring {
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: 50%;
|
||
border: 1px solid rgba(95, 191, 69, 0.28);
|
||
animation: pulse 2.4s ease-out infinite;
|
||
}
|
||
|
||
.r2 {
|
||
inset: -14px;
|
||
animation-delay: 0.35s;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.r3 {
|
||
inset: -28px;
|
||
animation-delay: 0.7s;
|
||
opacity: 0.4;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% {
|
||
transform: scale(0.86);
|
||
opacity: 0.55;
|
||
}
|
||
70% {
|
||
transform: scale(1.08);
|
||
opacity: 0;
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.hero-icon {
|
||
position: relative;
|
||
z-index: 1;
|
||
width: 72px;
|
||
height: 72px;
|
||
margin: 0 auto 16px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(145deg, #8fd96f 0%, #5fbf45 100%);
|
||
box-shadow: 0 10px 24px rgba(95, 191, 69, 0.35);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
animation: pop 0.5s cubic-bezier(0.34, 1.4, 0.64, 1);
|
||
}
|
||
|
||
@keyframes pop {
|
||
from {
|
||
transform: scale(0.6);
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
transform: scale(1);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.check {
|
||
color: #fff;
|
||
font-size: 34px;
|
||
font-weight: 700;
|
||
line-height: 1;
|
||
}
|
||
|
||
.hero-title {
|
||
margin: 0;
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #152016;
|
||
letter-spacing: 0.02em;
|
||
}
|
||
|
||
.hero-desc {
|
||
margin: 8px auto 0;
|
||
max-width: 300px;
|
||
font-size: 13px;
|
||
line-height: 1.55;
|
||
color: #7a8a76;
|
||
}
|
||
|
||
.thumb-card {
|
||
position: relative;
|
||
margin: 8px auto 14px;
|
||
width: min(100%, 220px);
|
||
aspect-ratio: 1;
|
||
border-radius: 18px;
|
||
overflow: hidden;
|
||
background: #fff;
|
||
box-shadow: 0 8px 22px rgba(40, 70, 50, 0.1);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.thumb-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 12px;
|
||
margin: 4px 0 14px;
|
||
}
|
||
|
||
.thumb-grid-item {
|
||
position: relative;
|
||
aspect-ratio: 1;
|
||
border-radius: 16px;
|
||
overflow: hidden;
|
||
background: #fff;
|
||
box-shadow: 0 6px 18px rgba(40, 70, 50, 0.1);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.thumb-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
display: block;
|
||
background: #fff;
|
||
}
|
||
|
||
.thumb-badge {
|
||
position: absolute;
|
||
left: 8px;
|
||
top: 8px;
|
||
padding: 3px 8px;
|
||
border-radius: 999px;
|
||
background: rgba(95, 191, 69, 0.92);
|
||
color: #fff;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.thumb-count {
|
||
position: absolute;
|
||
right: 8px;
|
||
top: 8px;
|
||
min-width: 28px;
|
||
padding: 3px 8px;
|
||
border-radius: 999px;
|
||
background: rgba(0, 0, 0, 0.55);
|
||
color: #fff;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
}
|
||
|
||
.thumb-label {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
padding: 18px 8px 8px;
|
||
font-size: 11px;
|
||
color: #fff;
|
||
background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.55));
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.steps-card,
|
||
.tips-card {
|
||
background: #fff;
|
||
border-radius: 16px;
|
||
padding: 16px 16px 14px;
|
||
margin-bottom: 12px;
|
||
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.04);
|
||
}
|
||
|
||
.steps-title,
|
||
.tips-title {
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
color: #1a1a1a;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.steps {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
.step {
|
||
position: relative;
|
||
display: flex;
|
||
gap: 12px;
|
||
padding: 0 0 16px;
|
||
}
|
||
|
||
.step:last-child {
|
||
padding-bottom: 0;
|
||
}
|
||
|
||
.step:not(:last-child)::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 7px;
|
||
top: 18px;
|
||
bottom: 0;
|
||
width: 2px;
|
||
background: #e6efe3;
|
||
}
|
||
|
||
.step .dot {
|
||
flex-shrink: 0;
|
||
width: 16px;
|
||
height: 16px;
|
||
margin-top: 2px;
|
||
border-radius: 50%;
|
||
background: #d7e4d3;
|
||
box-sizing: border-box;
|
||
border: 3px solid #eef5eb;
|
||
}
|
||
|
||
.step.is-done .dot,
|
||
.step.is-active .dot {
|
||
background: #5fbf45;
|
||
border-color: rgba(95, 191, 69, 0.22);
|
||
box-shadow: 0 0 0 3px rgba(95, 191, 69, 0.16);
|
||
}
|
||
|
||
.step-name {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #222;
|
||
}
|
||
|
||
.step-desc {
|
||
margin-top: 2px;
|
||
font-size: 12px;
|
||
color: #8a9586;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.tips-card p {
|
||
margin: 0;
|
||
font-size: 13px;
|
||
line-height: 1.55;
|
||
color: #6b7a66;
|
||
}
|
||
|
||
.footer {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
z-index: 5;
|
||
display: flex;
|
||
gap: 10px;
|
||
padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
|
||
background: linear-gradient(180deg, rgba(247, 250, 246, 0) 0%, #f7faf6 26%);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.btn-ghost {
|
||
flex: 0 0 112px;
|
||
height: 48px;
|
||
border: none;
|
||
border-radius: 24px;
|
||
background: #e4efe0;
|
||
color: #3f9e36;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.btn-primary {
|
||
flex: 1;
|
||
height: 48px;
|
||
border: none;
|
||
border-radius: 24px;
|
||
background: linear-gradient(90deg, #7ed45a 0%, #5fbf45 100%);
|
||
color: #fff;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
box-shadow: 0 6px 16px rgba(95, 191, 69, 0.32);
|
||
}
|
||
</style>
|