feat(aigc): add shared work page and AIGC sharing

- add new shared work page to view shared AIGC generated images and videos
- add createAigcGeneratorTaskShare and getAigcGeneratorTaskShareDetail APIs
- update ResultActions component to add share preparation state and native share support
- remove deprecated SharePreviewPopup component and its associated styles
- update AIGC detail page to generate share keys and adjust native share metadata
- register the new shared work page in pages.json
This commit is contained in:
duanshuwen
2026-07-16 13:28:46 +08:00
parent dcf67dedfc
commit 67b1b78f1f
8 changed files with 313 additions and 314 deletions

View File

@@ -0,0 +1,118 @@
<template>
<view class="shared-work-page">
<view class="shared-work-nav">
<TopNavBar title="旅行作品" background="transparent" title-color="#173a5f" back-icon-color="#173a5f"
:align-with-menu-button="true" :z-index="3" @back="handleBack" />
</view>
<view class="shared-work-scroll" scroll-y>
<template v-if="shareDetail">
<video v-if="isVideo" class="shared-work-media is-video" :src="shareDetail.videoUrl"
:poster="shareDetail.imageUrl" controls object-fit="cover" />
<image v-else-if="shareDetail.imageUrl" class="shared-work-media" :src="shareDetail.imageUrl" mode="widthFix" />
<view class="shared-work-meta">
<text class="shared-work-title">{{ shareDetail.templateName || "旅行作品" }}</text>
<text class="shared-work-type">{{ generatorTypeText }}</text>
</view>
</template>
</view>
<view class="shared-work-cta-wrap">
<button class="shared-work-cta" hover-class="is-pressed" @tap="handleCreateOwn">
我也生成一个
</button>
</view>
</view>
</template>
<script setup>
import { computed, ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import TopNavBar from "@/components/TopNavBar/index.vue";
import { checkToken } from "@/hooks/useGoLogin.js";
import { getAigcGeneratorTaskShareDetail } from "@/request/api/AigcApi.js";
const shareKey = ref("");
const shareDetail = ref(null);
const isVideo = computed(() => {
return Number(shareDetail.value?.generatorType) === 1 && Boolean(shareDetail.value?.videoUrl);
});
const generatorTypeText = computed(() => {
const generatorType = shareDetail.value?.generatorType;
if (Number(generatorType) === 0) return "图片版";
if (Number(generatorType) === 1) return "视频版";
return generatorType === undefined || generatorType === null || generatorType === ""
? "生成作品"
: `类型${generatorType}`;
});
const showToast = (title) => {
uni.showToast({
title,
icon: "none",
});
};
const fetchShareDetail = async () => {
uni.showLoading({
title: "加载中",
mask: true,
});
try {
const res = await getAigcGeneratorTaskShareDetail({ shareKey: shareKey.value });
if (res?.code === 0 && res.data && typeof res.data === "object" && !Array.isArray(res.data)) {
shareDetail.value = res.data;
return;
}
shareDetail.value = null;
showToast(res?.msg || "获取分享作品失败");
} catch (error) {
console.error("获取AIGC分享作品失败", error);
shareDetail.value = null;
showToast("获取分享作品失败");
} finally {
uni.hideLoading();
}
};
onLoad((query = {}) => {
shareKey.value = String(query.shareKey || "").trim();
if (!shareKey.value) {
showToast("缺少分享标识");
return;
}
fetchShareDetail();
});
const openAigcHome = () => {
uni.reLaunch({
url: "/pages-aigc/home/home",
fail: () => showToast("打开模板列表失败"),
});
};
const handleBack = () => {
const pages = getCurrentPages();
if (pages.length > 1) {
uni.navigateBack();
return;
}
openAigcHome();
};
const handleCreateOwn = async () => {
await checkToken();
openAigcHome();
};
</script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>

View File

@@ -0,0 +1,116 @@
.shared-work-page {
position: relative;
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
overflow: hidden;
color: #173a5f;
background: #c5e5f8;
box-sizing: border-box;
}
.shared-work-nav {
position: relative;
z-index: 3;
flex: 0 0 auto;
background: rgba(197, 229, 248, 0.96);
}
.shared-work-nav :deep(.top-nav-bar > .flex) {
padding-right: 12px;
padding-left: 12px;
}
.shared-work-nav :deep(.nav-bar-center text) {
font-size: 17px;
line-height: 23px;
font-weight: 900;
}
.shared-work-scroll {
flex: 1 1 0;
width: 100%;
height: 0;
min-height: 0;
padding-bottom: 104px;
box-sizing: border-box;
}
.shared-work-media {
width: 100%;
display: block;
background: #b8dced;
}
.shared-work-media.is-video {
height: min(500px, 66vh);
}
.shared-work-meta {
padding: 18px 18px 118px;
}
.shared-work-title,
.shared-work-type {
display: block;
}
.shared-work-title {
overflow: hidden;
color: #173a5f;
font-size: 20px;
line-height: 27px;
font-weight: 900;
text-overflow: ellipsis;
white-space: nowrap;
}
.shared-work-type {
margin-top: 4px;
color: #0b7b60;
font-size: 12px;
line-height: 17px;
font-weight: 800;
}
.shared-work-empty {
min-height: 420px;
display: flex;
align-items: center;
justify-content: center;
}
.shared-work-cta-wrap {
position: fixed;
z-index: 4;
right: 0;
bottom: 0;
left: 0;
padding: 18px 16px calc(14px + env(safe-area-inset-bottom));
background: linear-gradient(180deg, rgba(197, 229, 248, 0) 0%, #c5e5f8 34%);
box-sizing: border-box;
}
.shared-work-cta {
width: 100%;
height: 54px;
margin: 0;
padding: 0;
border: 0;
border-radius: 8px;
background: #20c99a;
color: #173a5f;
font-size: 16px;
line-height: 54px;
font-weight: 900;
box-shadow: 0 10px 22px rgba(32, 201, 154, 0.2);
}
.shared-work-cta::after {
border: 0;
}
.shared-work-cta.is-pressed {
opacity: 0.84;
}