Inspirations.vue 21.4 KB
Newer Older
LiangLiu's avatar
LiangLiu committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<script setup>
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import { watch, onMounted } from 'vue'

// Props
const props = defineProps({
  query: {
    type: Object,
    default: () => ({})
  },
  templateId: {
    type: String,
    default: null
  }
})

const { t, locale } = useI18n()
const route = useRoute()
const router = useRouter()
import {
            goToInspirationPage,
            getVisibleInspirationPages,
            getTemplateFileUrl,
            handleThumbnailError,
            inspirationSearchQuery,
            selectedInspirationCategory,
            inspirationItems,
            InspirationCategories,
            selectInspirationCategory,
            handleInspirationSearch,
            inspirationPaginationInfo,
            inspirationCurrentPage,
            previewTemplateDetail,
            useTemplate,
            applyTemplateImage,
            applyTemplateAudio,
            playVideo,
            pauseVideo,
            toggleVideoPlay,
            onVideoLoaded,
            onVideoError,
            onVideoEnded,
            openTemplateFromRoute,
LiangLiu's avatar
LiangLiu committed
45
46
            copyShareLink,
            isPageLoading
LiangLiu's avatar
LiangLiu committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
        } from '../utils/other'

// 监听模板详情路由
watch(() => route.params.templateId, (newTemplateId) => {
    if (newTemplateId && route.name === 'TemplateDetail') {
        openTemplateFromRoute(newTemplateId)
    }
}, { immediate: true })

// 路由监听和URL同步
watch(() => route.query, (newQuery) => {
    // 同步URL参数到组件状态
    if (newQuery.search) {
        inspirationSearchQuery.value = newQuery.search
    }
    if (newQuery.category) {
        selectedInspirationCategory.value = newQuery.category
    }
    if (newQuery.page) {
        const page = parseInt(newQuery.page)
        if (page > 0 && page !== inspirationCurrentPage.value) {
            goToInspirationPage(page)
        }
    }
}, { immediate: true })

// 监听组件状态变化,同步到URL
watch([inspirationSearchQuery, selectedInspirationCategory, inspirationCurrentPage], () => {
    const query = {}
    if (inspirationSearchQuery.value) {
        query.search = inspirationSearchQuery.value
    }
    if (selectedInspirationCategory.value && selectedInspirationCategory.value !== 'all') {
        query.category = selectedInspirationCategory.value
    }
    if (inspirationCurrentPage.value > 1) {
        query.page = inspirationCurrentPage.value.toString()
    }

    // 更新URL但不触发路由监听
    router.replace({ query })
})

// 组件挂载时初始化
onMounted(() => {
    // 确保URL参数正确同步
    const query = route.query
    if (query.search) {
        inspirationSearchQuery.value = query.search
    }
    if (query.category) {
        selectedInspirationCategory.value = query.category
    }
    if (query.page) {
        const page = parseInt(query.page)
        if (page > 0) {
            goToInspirationPage(page)
        }
    }
})

</script>
<template>
LiangLiu's avatar
LiangLiu committed
110
    <!-- 灵感广场区域 - Apple 极简风格 -->
LiangLiu's avatar
LiangLiu committed
111
112
113
114
                        <div class="flex-1 flex flex-col min-h-0 mobile-content">
                            <!-- 内容区域 -->
                            <div class="flex-1 overflow-y-auto p-6 content-area main-scrollbar">
                                <!-- 灵感广场功能区 -->
LiangLiu's avatar
LiangLiu committed
115
116
117
118
119
            <div class="max-w-7xl mx-auto" id="inspiration-gallery">
                <!-- 标题区域 - Apple 风格 -->
                <div class="text-center mb-10">
                    <h1 class="text-4xl sm:text-5xl font-semibold text-[#1d1d1f] dark:text-[#f5f5f7] mb-3 tracking-tight">{{ t('inspirationGallery') }}</h1>
                    <p class="text-base text-[#86868b] dark:text-[#98989d] tracking-tight">{{ t('discoverCreativity') }}</p>
LiangLiu's avatar
LiangLiu committed
120
121
                                        </div>

LiangLiu's avatar
LiangLiu committed
122
123
124
                <!-- 搜索和筛选区域 - Apple 风格 -->
                <div class="flex flex-col md:flex-row gap-4 mb-8">
                    <!-- 搜索框 - Apple 风格 -->
LiangLiu's avatar
LiangLiu committed
125
                                            <div class="relative flex-1">
LiangLiu's avatar
LiangLiu committed
126
                        <i class="fas fa-search absolute left-4 top-1/2 -translate-y-1/2 text-[#86868b] dark:text-[#98989d] pointer-events-none z-10"></i>
LiangLiu's avatar
LiangLiu committed
127
128
129
                                                    <input v-model="inspirationSearchQuery"
                                                    @keyup.enter="handleInspirationSearch"
                                                    @input="handleInspirationSearch"
LiangLiu's avatar
LiangLiu committed
130
131
132
                            class="w-full bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] border border-black/8 dark:border-white/8 rounded-xl py-3 pl-11 pr-4 text-[15px] text-[#1d1d1f] dark:text-[#f5f5f7] placeholder-[#86868b] dark:placeholder-[#98989d] tracking-tight hover:bg-white dark:hover:bg-[#3a3a3c] hover:border-black/12 dark:hover:border-white/12 focus:outline-none focus:border-[color:var(--brand-primary)]/50 dark:focus:border-[color:var(--brand-primary-light)]/60 focus:shadow-[0_4px_16px_rgba(var(--brand-primary-rgb),0.12)] dark:focus:shadow-[0_4px_16px_rgba(var(--brand-primary-light-rgb),0.2)] transition-all duration-200"
                            :placeholder="t('searchInspiration')"
                            type="text" />
LiangLiu's avatar
LiangLiu committed
133
134
                                            </div>

LiangLiu's avatar
LiangLiu committed
135
                    <!-- 分类筛选 - Apple 风格 -->
LiangLiu's avatar
LiangLiu committed
136
                                                <div class="flex gap-2 flex-wrap">
LiangLiu's avatar
LiangLiu committed
137
138
139
140
141
142
143
144
145
                        <!-- "全部"按钮 -->
                        <button @click="selectInspirationCategory('')"
                            class="px-5 py-2.5 text-sm font-medium rounded-full transition-all duration-200 tracking-tight"
                            :class="selectedInspirationCategory === '' || !selectedInspirationCategory
                                ? 'bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] text-white shadow-[0_4px_12px_rgba(var(--brand-primary-rgb),0.25)] dark:shadow-[0_4px_12px_rgba(var(--brand-primary-light-rgb),0.3)]'
                                : 'bg-white/80 dark:bg-[#2c2c2e]/80 border border-black/8 dark:border-white/8 text-[#86868b] dark:text-[#98989d] hover:bg-white dark:hover:bg-[#3a3a3c] hover:text-[#1d1d1f] dark:hover:text-[#f5f5f7]'">
                            {{ t('all') }}
                        </button>
                        <!-- 其他分类按钮 -->
LiangLiu's avatar
LiangLiu committed
146
147
                                                    <button v-for="category in InspirationCategories" :key="category"
                                                    @click="selectInspirationCategory(category)"
LiangLiu's avatar
LiangLiu committed
148
                            class="px-5 py-2.5 text-sm font-medium rounded-full transition-all duration-200 tracking-tight"
LiangLiu's avatar
LiangLiu committed
149
                                                    :class="selectedInspirationCategory === category
LiangLiu's avatar
LiangLiu committed
150
151
                                ? 'bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] text-white shadow-[0_4px_12px_rgba(var(--brand-primary-rgb),0.25)] dark:shadow-[0_4px_12px_rgba(var(--brand-primary-light-rgb),0.3)]'
                                : 'bg-white/80 dark:bg-[#2c2c2e]/80 border border-black/8 dark:border-white/8 text-[#86868b] dark:text-[#98989d] hover:bg-white dark:hover:bg-[#3a3a3c] hover:text-[#1d1d1f] dark:hover:text-[#f5f5f7]'">
LiangLiu's avatar
LiangLiu committed
152
153
154
155
                                                    {{ category }}
                                                </button>
                                            </div>
                                        </div>
LiangLiu's avatar
LiangLiu committed
156
157
158
159
160

                <!-- 灵感广场分页组件 - Apple 风格 -->
                <div v-if="inspirationPaginationInfo" class="mb-6">
                    <div class="flex items-center justify-between text-xs mb-4">
                        <div class="flex items-center space-x-1 text-[#86868b] dark:text-[#98989d] tracking-tight">
LiangLiu's avatar
LiangLiu committed
161
162
163
164
                                                        <span>{{ inspirationPaginationInfo.total }} {{ t('records') }}</span>
                                                    </div>
                                                </div>
                                                <div v-if="inspirationPaginationInfo.total_pages > 1" class="flex justify-center">
LiangLiu's avatar
LiangLiu committed
165
                        <nav class="isolate inline-flex gap-1" aria-label="Pagination">
LiangLiu's avatar
LiangLiu committed
166
167
168
                                                        <!-- 上一页按钮 -->
                                                        <button @click="goToInspirationPage(inspirationCurrentPage - 1)"
                                                            :disabled="inspirationCurrentPage <= 1"
LiangLiu's avatar
LiangLiu committed
169
                                class="relative inline-flex items-center w-9 h-9 rounded-lg bg-white/80 dark:bg-[#2c2c2e]/80 border border-black/8 dark:border-white/8 text-[#86868b] dark:text-[#98989d] hover:bg-white dark:hover:bg-[#3a3a3c] hover:text-[#1d1d1f] dark:hover:text-[#f5f5f7] transition-all duration-200"
LiangLiu's avatar
LiangLiu committed
170
171
172
                                                            :class="{ 'opacity-50 cursor-not-allowed': inspirationCurrentPage <= 1 }"
                                                            :title="t('previousPage')">
                                                            <span class="sr-only">{{ t('previousPage') }}</span>
LiangLiu's avatar
LiangLiu committed
173
                                <i class="fas fa-chevron-left text-xs mx-auto" aria-hidden="true"></i>
LiangLiu's avatar
LiangLiu committed
174
175
176
177
178
179
                                                        </button>

                                                        <!-- 页码按钮 -->
                                                        <template v-for="page in getVisibleInspirationPages()" :key="page">
                                                            <button v-if="page !== '...'" @click="goToInspirationPage(page)"
                                                                :class="[
LiangLiu's avatar
LiangLiu committed
180
                                        'relative inline-flex items-center justify-center min-w-[36px] h-9 px-3 text-sm font-medium rounded-lg transition-all duration-200',
LiangLiu's avatar
LiangLiu committed
181
                                                                    page === inspirationCurrentPage
LiangLiu's avatar
LiangLiu committed
182
183
                                            ? 'bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] text-white shadow-[0_2px_8px_rgba(var(--brand-primary-rgb),0.25)] dark:shadow-[0_2px_8px_rgba(var(--brand-primary-light-rgb),0.3)]'
                                            : 'bg-white/80 dark:bg-[#2c2c2e]/80 border border-black/8 dark:border-white/8 text-[#86868b] dark:text-[#98989d] hover:bg-white dark:hover:bg-[#3a3a3c] hover:text-[#1d1d1f] dark:hover:text-[#f5f5f7]'
LiangLiu's avatar
LiangLiu committed
184
185
186
187
                                                                ]"
                                                                :aria-current="page === inspirationCurrentPage ? 'page' : undefined">
                                                                {{ page }}
                                                            </button>
LiangLiu's avatar
LiangLiu committed
188
                                <span v-else class="relative inline-flex items-center px-2 text-sm font-semibold text-[#86868b] dark:text-[#98989d]">...</span>
LiangLiu's avatar
LiangLiu committed
189
190
191
192
193
                                                        </template>

                                                        <!-- 下一页按钮 -->
                                                        <button @click="goToInspirationPage(inspirationCurrentPage + 1)"
                                                            :disabled="inspirationCurrentPage >= inspirationPaginationInfo.total_pages"
LiangLiu's avatar
LiangLiu committed
194
                                class="relative inline-flex items-center w-9 h-9 rounded-lg bg-white/80 dark:bg-[#2c2c2e]/80 border border-black/8 dark:border-white/8 text-[#86868b] dark:text-[#98989d] hover:bg-white dark:hover:bg-[#3a3a3c] hover:text-[#1d1d1f] dark:hover:text-[#f5f5f7] transition-all duration-200"
LiangLiu's avatar
LiangLiu committed
195
196
197
                                                            :class="{ 'opacity-50 cursor-not-allowed': inspirationCurrentPage >= inspirationPaginationInfo.total_pages }"
                                                            :title="t('nextPage')">
                                                            <span class="sr-only">{{ t('nextPage') }}</span>
LiangLiu's avatar
LiangLiu committed
198
                                <i class="fas fa-chevron-right text-xs mx-auto" aria-hidden="true"></i>
LiangLiu's avatar
LiangLiu committed
199
200
201
202
                                                        </button>
                                                    </nav>
                                                </div>
                                        </div>
LiangLiu's avatar
LiangLiu committed
203
204

                <!-- 灵感内容网格 - Apple 风格 -->
LiangLiu's avatar
LiangLiu committed
205
206
207
208
209
210
211
212
                <div class="space-y-4">
                    <div v-if="isPageLoading" class="flex items-center justify-center">
                        <div class="inline-flex items-center gap-3 px-4 py-2 rounded-full bg-white/90 dark:bg-[#2c2c2e]/90 border border-black/8 dark:border-white/8 text-sm text-[#1d1d1f] dark:text-[#f5f5f7] shadow-[0_4px_16px_rgba(0,0,0,0.08)] dark:shadow-[0_4px_16px_rgba(0,0,0,0.35)]">
                            <i class="fas fa-spinner fa-spin text-[color:var(--brand-primary)] dark:text-[color:var(--brand-primary-light)]"></i>
                            <span>{{ t('loading') }}</span>
                        </div>
                    </div>
                    <div class="columns-2 md:columns-3 lg:columns-4 xl:columns-5 gap-4">
LiangLiu's avatar
LiangLiu committed
213
                    <!-- 灵感卡片 - Apple 风格 -->
LiangLiu's avatar
LiangLiu committed
214
                                            <div v-for="item in inspirationItems" :key="item.task_id"
LiangLiu's avatar
LiangLiu committed
215
                        class="break-inside-avoid mb-4 group relative bg-white/80 dark:bg-[#2c2c2e]/80 backdrop-blur-[20px] rounded-2xl overflow-hidden border border-black/8 dark:border-white/8 hover:border-[color:var(--brand-primary)]/30 dark:hover:border-[color:var(--brand-primary-light)]/30 hover:bg-white dark:hover:bg-[#3a3a3c] transition-all duration-200 hover:shadow-[0_8px_24px_rgba(var(--brand-primary-rgb),0.15)] dark:hover:shadow-[0_8px_24px_rgba(var(--brand-primary-light-rgb),0.2)]">
LiangLiu's avatar
LiangLiu committed
216
                                                <!-- 视频缩略图区域 -->
LiangLiu's avatar
LiangLiu committed
217
                        <div class="cursor-pointer bg-black/2 dark:bg-white/2 relative flex flex-col"
LiangLiu's avatar
LiangLiu committed
218
219
220
221
222
                                                @click="previewTemplateDetail(item)"
                                                :title="t('viewTemplateDetail')">
                                                        <!-- 视频预览 -->
                                                        <video v-if="item?.outputs?.output_video"
                                                            :src="getTemplateFileUrl(item.outputs.output_video,'videos')"
223
                                                            :poster="item?.inputs?.input_image ? getTemplateFileUrl(item.inputs.input_image,'images') : undefined"
LiangLiu's avatar
LiangLiu committed
224
                                class="w-full h-auto object-contain group-hover:scale-[1.02] transition-transform duration-200"
LiangLiu's avatar
LiangLiu committed
225
226
227
228
229
230
                                                            preload="auto" playsinline webkit-playsinline
                                                            @mouseenter="playVideo($event)" @mouseleave="pauseVideo($event)"
                                                            @loadeddata="onVideoLoaded($event)"
                                                            @ended="onVideoEnded($event)"
                                                            @error="onVideoError($event)"></video>
                                                    <!-- 图片缩略图 -->
231
                                                        <img v-else-if="item?.inputs?.input_image"
LiangLiu's avatar
LiangLiu committed
232
233
                                                        :src="getTemplateFileUrl(item.inputs.input_image,'images')"
                                                        :alt="item.params?.prompt || '模板图片'"
LiangLiu's avatar
LiangLiu committed
234
                                class="w-full h-auto object-contain group-hover:scale-[1.02] transition-transform duration-200"
LiangLiu's avatar
LiangLiu committed
235
                                                        @error="handleThumbnailError" />
LiangLiu's avatar
LiangLiu committed
236
237

                            <!-- 移动端播放按钮 - Apple 风格 -->
LiangLiu's avatar
LiangLiu committed
238
239
                                                        <button v-if="item?.outputs?.output_video"
                                                            @click.stop="toggleVideoPlay($event)"
LiangLiu's avatar
LiangLiu committed
240
                                class="md:hidden absolute bottom-3 left-1/2 transform -translate-x-1/2 w-10 h-10 rounded-full bg-white/95 dark:bg-[#2c2c2e]/95 backdrop-blur-[20px] shadow-[0_2px_8px_rgba(0,0,0,0.2)] dark:shadow-[0_2px_8px_rgba(0,0,0,0.4)] flex items-center justify-center text-[#1d1d1f] dark:text-[#f5f5f7] hover:scale-105 transition-all duration-200 z-20">
LiangLiu's avatar
LiangLiu committed
241
242
                                                            <i class="fas fa-play text-sm"></i>
                                                        </button>
LiangLiu's avatar
LiangLiu committed
243
244
245
246

                            <!-- 悬浮操作按钮(下方居中,仅桌面端)- Apple 风格 -->
                            <div class="hidden md:flex absolute bottom-3 left-1/2 transform -translate-x-1/2 items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-10 w-full">
                                <div class="flex gap-2 pointer-events-auto">
LiangLiu's avatar
LiangLiu committed
247
                                                            <button @click.stop="applyTemplateImage(item)"
LiangLiu's avatar
LiangLiu committed
248
                                        class="w-10 h-10 rounded-full bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] backdrop-blur-[20px] shadow-[0_2px_8px_rgba(var(--brand-primary-rgb),0.3)] dark:shadow-[0_2px_8px_rgba(var(--brand-primary-light-rgb),0.4)] flex items-center justify-center text-white hover:scale-110 active:scale-100 transition-all duration-200"
LiangLiu's avatar
LiangLiu committed
249
250
251
252
                                                                :title="t('applyImage')">
                                                                <i class="fas fa-image text-sm"></i>
                                                            </button>
                                                            <button @click.stop="applyTemplateAudio(item)"
LiangLiu's avatar
LiangLiu committed
253
                                        class="w-10 h-10 rounded-full bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] backdrop-blur-[20px] shadow-[0_2px_8px_rgba(var(--brand-primary-rgb),0.3)] dark:shadow-[0_2px_8px_rgba(var(--brand-primary-light-rgb),0.4)] flex items-center justify-center text-white hover:scale-110 active:scale-100 transition-all duration-200"
LiangLiu's avatar
LiangLiu committed
254
255
256
257
                                                                :title="t('applyAudio')">
                                                                <i class="fas fa-music text-sm"></i>
                                                            </button>
                                                            <button @click.stop="useTemplate(item)"
LiangLiu's avatar
LiangLiu committed
258
                                        class="w-10 h-10 rounded-full bg-[color:var(--brand-primary)] dark:bg-[color:var(--brand-primary-light)] backdrop-blur-[20px] shadow-[0_2px_8px_rgba(var(--brand-primary-rgb),0.3)] dark:shadow-[0_2px_8px_rgba(var(--brand-primary-light-rgb),0.4)] flex items-center justify-center text-white hover:scale-110 active:scale-100 transition-all duration-200"
LiangLiu's avatar
LiangLiu committed
259
260
261
262
                                                                :title="t('useTemplate')">
                                                                <i class="fas fa-clone text-sm"></i>
                                                            </button>
                                                            <button @click.stop="copyShareLink(item.task_id, 'template')"
LiangLiu's avatar
LiangLiu committed
263
                                        class="w-10 h-10 rounded-full bg-white dark:bg-[#3a3a3c] backdrop-blur-[20px] shadow-[0_2px_8px_rgba(0,0,0,0.12)] dark:shadow-[0_2px_8px_rgba(0,0,0,0.4)] flex items-center justify-center text-[#1d1d1f] dark:text-[#f5f5f7] hover:scale-110 active:scale-100 transition-all duration-200"
LiangLiu's avatar
LiangLiu committed
264
265
266
267
268
269
                                                                :title="t('shareTemplate')">
                                                                <i class="fas fa-share-alt text-sm"></i>
                                                            </button>
                                </div>
                            </div>
                        </div>
LiangLiu's avatar
LiangLiu committed
270
                    </div>
LiangLiu's avatar
LiangLiu committed
271
                    </div>
LiangLiu's avatar
LiangLiu committed
272
273
274
275
276
277
278
279
280
281
282
283
284
285
                </div>

                <!-- GitHub 仓库链接 - Apple 极简风格 -->
                <div class="fixed bottom-6 right-6 z-50">
                    <a href="https://github.com/ModelTC/LightX2V"
                       target="_blank"
                       rel="noopener noreferrer"
                       class="flex items-center gap-2.5 px-4 py-2.5 bg-white/85 dark:bg-[#1e1e1e]/85 backdrop-blur-[40px] border border-black/10 dark:border-white/10 rounded-full shadow-[0_4px_16px_rgba(0,0,0,0.1)] dark:shadow-[0_4px_16px_rgba(0,0,0,0.3)] hover:shadow-[0_8px_24px_rgba(0,0,0,0.15)] dark:hover:shadow-[0_8px_24px_rgba(0,0,0,0.4)] hover:scale-105 active:scale-100 transition-all duration-200 group"
                       title="Star us on GitHub">
                        <i class="fab fa-github text-lg text-[#1d1d1f] dark:text-[#f5f5f7] transition-transform duration-200 group-hover:rotate-12"></i>
                        <span class="text-sm font-medium text-[#1d1d1f] dark:text-[#f5f5f7] tracking-tight">LightX2V</span>
                        <i class="fas fa-external-link-alt text-xs text-[#86868b] dark:text-[#98989d] transition-all duration-200 group-hover:translate-x-0.5 group-hover:-translate-y-0.5"></i>
                    </a>
                        </div>
LiangLiu's avatar
LiangLiu committed
286
287
288
289
                        </div>
                </div>
        </div>
</template>