|
- <template>
- <view class="page">
- <scroll-view class="scroll" scroll-y>
- <view class="wrap">
- <view v-if="isLoadingState" class="state-card">
- <view v-if="detail.is_suc === 0" class="spinner"></view>
- <text class="state-title">{{ detail.is_suc === 0 ? '创作中,请耐心等待' : '生成失败' }}</text>
- <text v-if="detail.is_suc === 0 && countDown > 0" class="count">{{ countDown }} S</text>
- <text v-if="detail.is_suc < 0" class="error">{{ detail.content }}</text>
- </view>
-
- <view v-else class="image-card" @click="preview">
- <image class="work-img" :src="detail.picUrl" mode="widthFix" />
- <view v-if="detail.picList && detail.picList.length > 1" class="num">{{ detail.picList.length }}</view>
- </view>
-
- <view class="info-card">
- <view class="title">作品信息</view>
- <view v-if="detail.prompts" class="cell">
- <text class="cell-label">画面描述:</text>
- <text class="cell-content" user-select>{{ detail.prompts }}</text>
- </view>
- <view class="cell">
- <text class="cell-label">作者:</text>
- <text class="cell-content">{{ detail.author_name || '-' }}</text>
- </view>
- <view class="cell">
- <text class="cell-label">作品来源:</text>
- <text class="link" @click="createAgain">{{ detail.ai_type_name || aiTypeName }}</text>
- </view>
- </view>
- </view>
- </scroll-view>
-
- <view v-if="!isLoadingState" class="bottom-bar">
- <view class="bar-btn" @click="download">下载</view>
- <button class="bar-btn" open-type="share">分享</button>
- <view class="main-btn" @click="createAgain">再画一张</view>
- </view>
- </view>
- </template>
-
- <script setup>
- import { computed, ref } from 'vue'
- import { onLoad, onShareAppMessage, onUnload } from '@dcloudio/uni-app'
- import { toolApi } from '@/api/index.js'
- import { useApp } from '@/utils/useApp.js'
-
- const { toast } = useApp()
- const id = ref('')
- const imgIndex = ref(0)
- const from = ref('')
- const detail = ref({ picList: [], is_suc: 0 })
- const countDown = ref(0)
- let timer = 0
- let countTimer = 0
-
- const aiTypeName = computed(() => {
- const map = { 3: '动漫头像', 7: '动漫头像', 8: '动漫头像', 2: 'MJ绘画', 6: 'AI绘画' }
- return map[detail.value.ai_type] || 'AI绘画'
- })
-
- const isLoadingState = computed(() => [0, -1, -2].includes(Number(detail.value.is_suc)))
-
- onLoad((options) => {
- const parts = String(options.id || '').split('_')
- id.value = parts[0] || ''
- imgIndex.value = Number(parts[1] || 0)
- from.value = options.from || ''
- loadDetail()
- })
-
- onUnload(() => {
- clearTimeout(timer)
- clearInterval(countTimer)
- })
-
- onShareAppMessage(() => ({
- title: '推荐给你看一个 AI 绘画作品',
- path: `/pages/tool/work-detail?id=${id.value}_0`,
- imageUrl: detail.value.picUrl || ''
- }))
-
- async function loadDetail() {
- if (!id.value) return
- try {
- const api = from.value === 'anime' ? toolApi.getAnimeDetail : toolApi.getWorkDetail
- const res = await api({ chat_open_id: id.value })
- const data = res.data || {}
- const pics = buildPicList(data)
- detail.value = {
- ...data,
- picList: pics,
- picUrl: pics[imgIndex.value] || pics[0] || data.content || '',
- ai_type_name: data.ai_type_name || aiTypeName.value
- }
- if (Number(detail.value.is_suc) === 0) waitNext()
- } catch (e) {
- toast(e.msg || '作品详情加载失败')
- }
- }
-
- function buildPicList(data) {
- if (Array.isArray(data.picList) && data.picList.length) return data.picList
- if (data.imgs) return String(data.imgs).split(',').filter(Boolean)
- if (data.content && String(data.content).includes(',')) return String(data.content).split(',').filter(Boolean)
- return data.content ? [data.content] : []
- }
-
- function waitNext() {
- clearTimeout(timer)
- clearInterval(countTimer)
- countDown.value = 3
- countTimer = setInterval(() => {
- countDown.value -= 1
- if (countDown.value <= 0) clearInterval(countTimer)
- }, 1000)
- timer = setTimeout(loadDetail, 3000)
- }
-
- function preview() {
- const urls = detail.value.picList || []
- if (!urls.length) return
- uni.previewImage({ urls, current: detail.value.picUrl })
- }
-
- function download() {
- if (!detail.value.picUrl) return
- uni.previewImage({ urls: [detail.value.picUrl] })
- }
-
- function createAgain() {
- const delta = getPageStackDelta('pages/tool/anime-avatar')
- if (delta > 0) {
- if (id.value) uni.setStorageSync('anime_copy_id', id.value)
- uni.navigateBack({ delta })
- return
- }
- uni.navigateTo({ url: `/pages/tool/anime-avatar?copy_id=${id.value}` })
- }
-
- function getPageStackDelta(routeName) {
- const pages = getCurrentPages()
- const index = pages.findIndex((page) => page.route === routeName)
- return index >= 0 ? pages.length - 1 - index : -1
- }
- </script>
-
- <style lang="scss" scoped>
- .page {
- height: 100vh;
- background: #f4f7fb;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- }
-
- .scroll {
- flex: 1;
- height: 0;
- }
-
- .wrap {
- padding: 28rpx 28rpx 150rpx;
- }
-
- .state-card,
- .image-card,
- .info-card {
- background: #fff;
- border-radius: 18rpx;
- overflow: hidden;
- }
-
- .state-card {
- min-height: 540rpx;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- color: #172033;
- font-size: 30rpx;
- }
-
- .spinner {
- width: 72rpx;
- height: 72rpx;
- border: 8rpx solid #e9edf5;
- border-top-color: #0b8cff;
- border-radius: 50%;
- animation: spin 0.8s linear infinite;
- margin-bottom: 28rpx;
- }
-
- .count,
- .error {
- margin-top: 18rpx;
- color: #7f8896;
- font-size: 26rpx;
- }
-
- .error {
- color: #f04438;
- padding: 0 40rpx;
- text-align: center;
- }
-
- .work-img {
- width: 100%;
- display: block;
- }
-
- .image-card {
- position: relative;
- }
-
- .num {
- position: absolute;
- right: 18rpx;
- bottom: 18rpx;
- min-width: 44rpx;
- height: 44rpx;
- border-radius: 22rpx;
- color: #fff;
- font-size: 24rpx;
- line-height: 44rpx;
- text-align: center;
- background: rgba(0, 0, 0, 0.45);
- }
-
- .info-card {
- margin-top: 24rpx;
- padding: 28rpx;
- }
-
- .title {
- color: #172033;
- font-size: 32rpx;
- font-weight: 700;
- margin-bottom: 16rpx;
- }
-
- .cell {
- display: flex;
- color: #7f8896;
- font-size: 27rpx;
- line-height: 1.6;
- padding: 10rpx 0;
- }
-
- .cell-label {
- flex-shrink: 0;
- }
-
- .cell-content {
- flex: 1;
- min-width: 0;
- color: #172033;
- }
-
- .link {
- color: #0b8cff;
- }
-
- .bottom-bar {
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- padding: 18rpx 28rpx calc(18rpx + env(safe-area-inset-bottom));
- background: #fff;
- display: flex;
- gap: 18rpx;
- box-shadow: 0 -8rpx 24rpx rgba(35, 55, 90, 0.08);
- }
-
- .bar-btn,
- .main-btn {
- height: 82rpx;
- border-radius: 42rpx;
- font-size: 29rpx;
- margin: 0;
- padding: 0;
- border: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .bar-btn::after {
- border: 0;
- }
-
- .bar-btn {
- width: 150rpx;
- color: #0b8cff;
- background: #eef6ff;
- }
-
- .main-btn {
- flex: 1;
- color: #fff;
- background: #0b8cff;
- }
-
- @keyframes spin {
- to {
- transform: rotate(360deg);
- }
- }
- </style>
|