|
- <template>
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 sm:py-12">
- <h1 class="text-2xl sm:text-3xl font-bold text-slate-900 mb-2">我的作品</h1>
- <p class="text-sm text-slate-500 mb-8">查看你所有的 AI 创作成果</p>
-
- <!-- Tab 分类 -->
- <div class="flex gap-2 overflow-x-auto pb-2 mb-6">
- <button
- v-for="tab in tabs"
- :key="tab.key"
- :class="[
- 'shrink-0 px-4 py-2 text-sm rounded-lg transition-colors cursor-pointer',
- activeTab === tab.key
- ? 'bg-indigo-500 text-white font-medium'
- : 'bg-white text-slate-600 border border-slate-200 hover:bg-slate-50',
- ]"
- @click="activeTab = tab.key"
- >
- {{ tab.label }}
- </button>
- </div>
-
- <!-- 加载中 -->
- <div v-if="loading" class="flex justify-center py-20">
- <svg class="w-8 h-8 text-indigo-500 animate-spin" fill="none" viewBox="0 0 24 24">
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
- </svg>
- </div>
-
- <!-- 作品网格 -->
- <div v-else-if="works.length > 0" class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4">
- <div
- v-for="work in works"
- :key="work.id"
- class="group bg-white rounded-xl shadow-sm border border-slate-100 overflow-hidden hover:shadow-md transition-all duration-200"
- >
- <div class="aspect-square bg-slate-100 relative overflow-hidden">
- <img
- v-if="work.cover_url || work.url"
- :src="work.cover_url || work.url"
- :alt="work.name"
- class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
- />
- <div
- v-else-if="work.status === 'processing'"
- class="w-full h-full flex items-center justify-center bg-indigo-50"
- >
- <svg class="w-8 h-8 text-indigo-400 animate-spin" fill="none" viewBox="0 0 24 24">
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
- </svg>
- </div>
- <div v-else class="w-full h-full flex items-center justify-center text-slate-300 text-sm">
- {{ work.type_name || '作品' }}
- </div>
- </div>
- <div class="p-3">
- <p class="text-sm font-medium text-slate-800 truncate">{{ work.name || '未命名' }}</p>
- <p class="mt-1 text-xs text-slate-400">{{ formatDate(work.created_at) }}</p>
- </div>
- </div>
- </div>
-
- <!-- 空状态 -->
- <div v-else class="text-center py-20">
- <svg class="w-16 h-16 mx-auto text-slate-200 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
- </svg>
- <p class="text-sm text-slate-500">暂无作品,快去创作吧!</p>
- </div>
-
- <!-- 加载更多 -->
- <div v-if="hasMore" class="flex justify-center mt-8">
- <button
- class="px-6 py-2.5 text-sm text-indigo-600 bg-indigo-50 hover:bg-indigo-100 rounded-lg transition-colors cursor-pointer"
- @click="loadMore"
- >
- 加载更多
- </button>
- </div>
- </div>
- </template>
-
- <script setup>
- useSeoMeta({
- title: '我的作品 - 奇想宇宙',
- description: '查看AI创作的所有作品',
- })
-
- const { isLogin } = useUser()
- const { post } = useApi()
-
- const tabs = [
- { label: '全部', key: 'all' },
- { label: 'AI取名', key: 'name' },
- { label: '动漫头像', key: 'avatar' },
- { label: 'AI绘画', key: 'draw' },
- ]
-
- const activeTab = ref('all')
- const loading = ref(true)
- const works = ref([])
- const page = ref(1)
- const hasMore = ref(false)
-
- // 格式化日期
- function formatDate(dateStr) {
- if (!dateStr) return ''
- return String(dateStr).substring(0, 10)
- }
-
- // 加载作品
- async function loadWorks(reset = false) {
- if (reset) {
- page.value = 1
- works.value = []
- }
-
- loading.value = true
- try {
- const res = await post('/tool/getworks', {
- type: activeTab.value === 'all' ? '' : activeTab.value,
- page: page.value,
- page_size: 20,
- client: 1,
- })
- if (res.code === 0) {
- const list = res.list || res.data || []
- if (reset) {
- works.value = list
- } else {
- works.value.push(...list)
- }
- hasMore.value = list.length >= 20
- }
- } catch {
- // 忽略
- } finally {
- loading.value = false
- }
- }
-
- // 加载更多
- function loadMore() {
- page.value++
- loadWorks(false)
- }
-
- // 切换分类
- watch(activeTab, () => {
- loadWorks(true)
- })
-
- onMounted(() => {
- if (isLogin.value) {
- loadWorks(true)
- }
- })
- </script>
|