feat(aigc): implement travel AIGC feature and clean up legacy assets

- Replace all local static AIGC assets with OSS hosted URLs to reduce bundle size
- Add travelAIGC command to ChatModel and update quick access menu to link to AIGC home
- Implement AIGC user consent flow with local storage persistence for compliance
- Refactor Discovery page layout, remove unused HeritageDiscoverySection component
- Update ChatTopNavBar to support dynamic navigation bar height based on system info
- Restructure ChatMainList layout for better discovery panel interaction
- Remove deprecated local asset files and clean up unused imports across codebase
This commit is contained in:
duanshuwen
2026-07-06 22:40:36 +08:00
parent bfda50e14a
commit 191ba48b9d
20 changed files with 268 additions and 383 deletions

View File

@@ -1,111 +0,0 @@
<template>
<view class="heritage-section">
<view class="heritage-carousel">
<scroll-view
class="heritage-scroll"
scroll-x
show-scrollbar="false"
:scroll-left="carouselScrollLeft"
>
<view class="heritage-track">
<view
v-for="(item, index) in normalizedCards"
:key="getItemKey(item, index)"
class="heritage-card"
:class="{
'is-featured': index === 1,
'is-selected': index === 1,
}"
hover-class="is-pressed"
hover-stay-time="80"
@tap="handleCardTap(item.raw, index)"
>
<view class="heritage-image">
<image
v-if="item.coverImage"
class="heritage-cover"
:src="item.coverImage"
mode="aspectFill"
/>
<text class="heritage-badge">{{ item.badge }}</text>
</view>
<view class="heritage-copy">
<text class="heritage-tag ellipsis-1">{{ item.tag }}</text>
<text class="heritage-title ellipsis-1">{{ item.title }}</text>
<text class="heritage-desc ellipsis-1">{{ item.subTitle }}</text>
</view>
</view>
</view>
</scroll-view>
</view>
<HeritageQuickPrompts
:list="prompts"
@didSelectItem="handlePromptTap"
/>
</view>
</template>
<script setup>
import { computed, ref, watch } from "vue";
import HeritageQuickPrompts from "../HeritageQuickPrompts/index.vue";
const props = defineProps({
cards: {
type: Array,
default: () => [],
},
prompts: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(["select-card", "select-prompt"]);
const carouselScrollLeft = ref(165);
watch(
() => props.cards.length,
(length) => {
const targetScrollLeft = length > 1 ? 165 : 0;
carouselScrollLeft.value = 0;
setTimeout(() => {
carouselScrollLeft.value = targetScrollLeft;
}, 0);
},
{ immediate: true }
);
const getBadgeText = (item) => {
const source = item.jumpDesc || item.tag || item.title || "";
return source.slice(0, 2) || "推荐";
};
const normalizedCards = computed(() =>
props.cards.map((item) => ({
raw: item,
id: item.id ?? item.tabContentId,
title: item.title || "",
subTitle: item.subTitle || "",
tag: item.tag || item.jumpDesc || "推荐",
badge: getBadgeText(item),
coverImage: item.coverImage || "",
}))
);
const getItemKey = (item, index) => item.id ?? item.title ?? index;
const handleCardTap = (item, index) => {
emit("select-card", item, index);
};
const handlePromptTap = (item, index) => {
emit("select-prompt", item, index);
};
</script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>

View File

@@ -1,137 +0,0 @@
.heritage-section {
width: 100%;
height: 386px;
overflow: hidden;
background: $theme-color-50;
}
.heritage-carousel,
.heritage-scroll {
width: 100%;
height: 268px;
overflow: hidden;
background: $theme-color-50;
}
.heritage-track {
display: inline-flex;
align-items: flex-start;
min-width: 100%;
height: 268px;
padding: 8px 69px 26px;
box-sizing: border-box;
}
.heritage-card {
flex: 0 0 189px;
width: 189px;
height: 227px;
margin-top: 5px;
margin-right: 14px;
display: flex;
flex-direction: column;
padding: 8px;
border-radius: 28px;
border: 1px solid #f1f5f9;
background: #ffffff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
box-sizing: border-box;
text-align: left;
}
.heritage-card:last-child {
margin-right: 0;
}
.heritage-card.is-featured {
flex-basis: 237px;
width: 237px;
height: 234px;
margin-top: 0;
}
.heritage-card.is-selected {
border-color: rgba(12, 205, 88, 0.55);
box-shadow: 0 8px 18px rgba(12, 205, 88, 0.12);
transform: translateY(-2px);
}
.heritage-card.is-pressed {
opacity: 0.92;
}
.heritage-image {
position: relative;
width: 100%;
height: 138px;
overflow: hidden;
border-radius: 20px;
background: #f1f5f9;
}
.heritage-card.is-featured .heritage-image {
height: 145px;
}
.heritage-cover {
width: 100%;
height: 100%;
display: block;
}
.heritage-badge {
position: absolute;
top: 8px;
right: 8px;
width: 34px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
background: rgba(255, 255, 255, 0.8);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
color: $theme-color-500;
font-size: 11px;
line-height: 16px;
font-weight: 900;
}
.heritage-copy {
min-width: 0;
margin-top: 8px;
padding: 0 8px;
}
.heritage-tag {
display: inline-flex;
max-width: 100%;
height: 18px;
align-items: center;
padding: 0 6px;
border-radius: 4px;
background: #fffbeb;
color: #d97706;
font-size: 9px;
line-height: 14px;
font-weight: 800;
}
.heritage-title {
display: block;
max-width: 100%;
margin: 4px 0;
color: #1e293b;
font-size: 16px;
line-height: 20px;
font-weight: 900;
}
.heritage-desc {
display: block;
max-width: 100%;
color: #94a3b8;
font-size: 10px;
line-height: 15px;
font-weight: 600;
}

View File

@@ -4,17 +4,18 @@
<FindTabs v-if="discoveryTabs.length > 0" v-model="activeIndex" :tabs="discoveryTabs" @change="handleTabChange" />
</view>
<HeritageDiscoverySection :cards="discoveryCards" :prompts="discoveryQuickQuestions" @select-card="handleCardClick"
@select-prompt="handleQuickQuestionClick" />
<scroll-view class="discovery-scroll" scroll-y @scroll="handleContentScroll">
<view class="discovery-body">
<ExploreCards v-if="discoveryCards.length > 0" :list="discoveryCards" @didSelectItem="handleCardClick" />
<ExploreCards v-if="discoveryCards.length > 0" :list="discoveryCards" @didSelectItem="handleCardClick" />
<QuickQuestions v-if="discoveryQuickQuestions.length > 0" :list="discoveryQuickQuestions"
@didSelectItem="handleQuickQuestionClick" />
<QuickQuestions v-if="discoveryQuickQuestions.length > 0" :list="discoveryQuickQuestions"
@didSelectItem="handleQuickQuestionClick" />
<DiscoveryPhotoToolCard @didSelectItem="handleToolCardClick" @didSelectOption="handleToolOptionClick" />
<!-- <DiscoveryPhotoToolCard @didSelectItem="handleToolCardClick" @didSelectOption="handleToolOptionClick" />
<DiscoveryAudioToolCard @didSelectItem="handleToolCardClick" />
<DiscoveryGuideMapToolCard @didSelectItem="handleToolCardClick" />
<DiscoveryGuideMapToolCard @didSelectItem="handleToolCardClick" /> -->
</view>
</scroll-view>
</view>
</template>
@@ -26,7 +27,6 @@ import { getAccessToken } from "@/constant/token";
import FindTabs from "./components/FindTabs/index.vue";
import ExploreCards from "./components/ExploreCards/index.vue";
import HeritageDiscoverySection from "./components/HeritageDiscoverySection/index.vue";
import QuickQuestions from "./components/QuickQuestions/index.vue";
import DiscoveryPhotoToolCard from "./components/DiscoveryPhotoToolCard/index.vue";
import DiscoveryAudioToolCard from "./components/DiscoveryAudioToolCard/index.vue";
@@ -45,7 +45,13 @@ const activeIndex = ref(0);
const discoveryTabs = ref([]);
const discoveryCards = ref([]);
const discoveryQuickQuestions = ref([]);
const emit = defineEmits(["scroll-touch-start", "scroll-touch"]);
const emit = defineEmits(["content-scroll"]);
const handleContentScroll = (e) => {
emit("content-scroll", {
scrollTop: Number(e.detail?.scrollTop || 0),
});
};
/// tabs 切换事件
@@ -208,16 +214,30 @@ onMounted(() => {
<style lang="scss" scoped>
.discovery-content {
display: flex;
flex-direction: column;
width: 100%;
min-height: 100%;
height: 100%;
min-height: 0;
background: $theme-color-50;
}
.discovery-tabs-sticky {
position: sticky;
top: 0;
flex-shrink: 0;
z-index: 12;
width: 100%;
background: $theme-color-50;
}
.discovery-scroll {
flex: 1;
width: 100%;
height: 0;
min-height: 0;
overflow: hidden;
}
.discovery-body {
width: 100%;
}
</style>