Inspirations.vue 20.6 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
45
46
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
<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,
            copyShareLink
        } 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
109
    <!-- 灵感广场区域 - Apple 极简风格 -->
LiangLiu's avatar
LiangLiu committed
110
111
112
113
                        <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
114
115
116
117
118
            <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
119
120
                                        </div>

LiangLiu's avatar
LiangLiu committed
121
122
123
                <!-- 搜索和筛选区域 - Apple 风格 -->
                <div class="flex flex-col md:flex-row gap-4 mb-8">
                    <!-- 搜索框 - Apple 风格 -->
LiangLiu's avatar
LiangLiu committed
124
                                            <div class="relative flex-1">
LiangLiu's avatar
LiangLiu committed
125
                        <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
126
127
128
                                                    <input v-model="inspirationSearchQuery"
                                                    @keyup.enter="handleInspirationSearch"
                                                    @input="handleInspirationSearch"
LiangLiu's avatar
LiangLiu committed
129
130
131
                            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
132
133
                                            </div>

LiangLiu's avatar
LiangLiu committed
134
                    <!-- 分类筛选 - Apple 风格 -->
LiangLiu's avatar
LiangLiu committed
135
                                                <div class="flex gap-2 flex-wrap">
LiangLiu's avatar
LiangLiu committed
136
137
138
139
140
141
142
143
144
                        <!-- "全部"按钮 -->
                        <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
145
146
                                                    <button v-for="category in InspirationCategories" :key="category"
                                                    @click="selectInspirationCategory(category)"
LiangLiu's avatar
LiangLiu committed
147
                            class="px-5 py-2.5 text-sm font-medium rounded-full transition-all duration-200 tracking-tight"
LiangLiu's avatar
LiangLiu committed
148
                                                    :class="selectedInspirationCategory === category
LiangLiu's avatar
LiangLiu committed
149
150
                                ? '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
151
152
153
154
                                                    {{ category }}
                                                </button>
                                            </div>
                                        </div>
LiangLiu's avatar
LiangLiu committed
155
156
157
158
159

                <!-- 灵感广场分页组件 - 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
160
161
162
163
                                                        <span>{{ inspirationPaginationInfo.total }} {{ t('records') }}</span>
                                                    </div>
                                                </div>
                                                <div v-if="inspirationPaginationInfo.total_pages > 1" class="flex justify-center">
LiangLiu's avatar
LiangLiu committed
164
                        <nav class="isolate inline-flex gap-1" aria-label="Pagination">
LiangLiu's avatar
LiangLiu committed
165
166
167
                                                        <!-- 上一页按钮 -->
                                                        <button @click="goToInspirationPage(inspirationCurrentPage - 1)"
                                                            :disabled="inspirationCurrentPage <= 1"
LiangLiu's avatar
LiangLiu committed
168
                                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
169
170
171
                                                            :class="{ 'opacity-50 cursor-not-allowed': inspirationCurrentPage <= 1 }"
                                                            :title="t('previousPage')">
                                                            <span class="sr-only">{{ t('previousPage') }}</span>
LiangLiu's avatar
LiangLiu committed
172
                                <i class="fas fa-chevron-left text-xs mx-auto" aria-hidden="true"></i>
LiangLiu's avatar
LiangLiu committed
173
174
175
176
177
178
                                                        </button>

                                                        <!-- 页码按钮 -->
                                                        <template v-for="page in getVisibleInspirationPages()" :key="page">
                                                            <button v-if="page !== '...'" @click="goToInspirationPage(page)"
                                                                :class="[
LiangLiu's avatar
LiangLiu committed
179
                                        '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
180
                                                                    page === inspirationCurrentPage
LiangLiu's avatar
LiangLiu committed
181
182
                                            ? '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
183
184
185
186
                                                                ]"
                                                                :aria-current="page === inspirationCurrentPage ? 'page' : undefined">
                                                                {{ page }}
                                                            </button>
LiangLiu's avatar
LiangLiu committed
187
                                <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
188
189
190
191
192
                                                        </template>

                                                        <!-- 下一页按钮 -->
                                                        <button @click="goToInspirationPage(inspirationCurrentPage + 1)"
                                                            :disabled="inspirationCurrentPage >= inspirationPaginationInfo.total_pages"
LiangLiu's avatar
LiangLiu committed
193
                                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
194
195
196
                                                            :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
197
                                <i class="fas fa-chevron-right text-xs mx-auto" aria-hidden="true"></i>
LiangLiu's avatar
LiangLiu committed
198
199
200
201
                                                        </button>
                                                    </nav>
                                                </div>
                                        </div>
LiangLiu's avatar
LiangLiu committed
202
203
204
205

                <!-- 灵感内容网格 - Apple 风格 -->
                <div class="columns-2 md:columns-3 lg:columns-4 xl:columns-5 gap-4">
                    <!-- 灵感卡片 - Apple 风格 -->
LiangLiu's avatar
LiangLiu committed
206
                                            <div v-for="item in inspirationItems" :key="item.task_id"
LiangLiu's avatar
LiangLiu committed
207
                        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
208
                                                <!-- 视频缩略图区域 -->
LiangLiu's avatar
LiangLiu committed
209
                        <div class="cursor-pointer bg-black/2 dark:bg-white/2 relative flex flex-col"
LiangLiu's avatar
LiangLiu committed
210
211
212
213
214
215
                                                @click="previewTemplateDetail(item)"
                                                :title="t('viewTemplateDetail')">
                                                        <!-- 视频预览 -->
                                                        <video v-if="item?.outputs?.output_video"
                                                            :src="getTemplateFileUrl(item.outputs.output_video,'videos')"
                                                            :poster="getTemplateFileUrl(item.inputs.input_image,'images')"
LiangLiu's avatar
LiangLiu committed
216
                                class="w-full h-auto object-contain group-hover:scale-[1.02] transition-transform duration-200"
LiangLiu's avatar
LiangLiu committed
217
218
219
220
221
222
223
224
225
                                                            preload="auto" playsinline webkit-playsinline
                                                            @mouseenter="playVideo($event)" @mouseleave="pauseVideo($event)"
                                                            @loadeddata="onVideoLoaded($event)"
                                                            @ended="onVideoEnded($event)"
                                                            @error="onVideoError($event)"></video>
                                                    <!-- 图片缩略图 -->
                                                        <img v-else
                                                        :src="getTemplateFileUrl(item.inputs.input_image,'images')"
                                                        :alt="item.params?.prompt || '模板图片'"
LiangLiu's avatar
LiangLiu committed
226
                                class="w-full h-auto object-contain group-hover:scale-[1.02] transition-transform duration-200"
LiangLiu's avatar
LiangLiu committed
227
                                                        @error="handleThumbnailError" />
LiangLiu's avatar
LiangLiu committed
228
229

                            <!-- 移动端播放按钮 - Apple 风格 -->
LiangLiu's avatar
LiangLiu committed
230
231
                                                        <button v-if="item?.outputs?.output_video"
                                                            @click.stop="toggleVideoPlay($event)"
LiangLiu's avatar
LiangLiu committed
232
                                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
233
234
                                                            <i class="fas fa-play text-sm"></i>
                                                        </button>
LiangLiu's avatar
LiangLiu committed
235
236
237
238

                            <!-- 悬浮操作按钮(下方居中,仅桌面端)- 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
239
                                                            <button @click.stop="applyTemplateImage(item)"
LiangLiu's avatar
LiangLiu committed
240
                                        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
241
242
243
244
                                                                :title="t('applyImage')">
                                                                <i class="fas fa-image text-sm"></i>
                                                            </button>
                                                            <button @click.stop="applyTemplateAudio(item)"
LiangLiu's avatar
LiangLiu committed
245
                                        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
246
247
248
249
                                                                :title="t('applyAudio')">
                                                                <i class="fas fa-music text-sm"></i>
                                                            </button>
                                                            <button @click.stop="useTemplate(item)"
LiangLiu's avatar
LiangLiu committed
250
                                        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
251
252
253
254
                                                                :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
255
                                        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
256
257
258
259
260
261
                                                                :title="t('shareTemplate')">
                                                                <i class="fas fa-share-alt text-sm"></i>
                                                            </button>
                                </div>
                            </div>
                        </div>
LiangLiu's avatar
LiangLiu committed
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
                    </div>
                </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
277
278
279
280
                        </div>
                </div>
        </div>
</template>