feat: 图片广场优化
This commit is contained in:
@@ -13,11 +13,12 @@ import {
|
||||
Search,
|
||||
User,
|
||||
WarningFilled,
|
||||
ZoomIn,
|
||||
} from '@element-plus/icons-vue';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { format } from 'date-fns';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
|
||||
import { getImagePlaza } from '@/api/aiImage';
|
||||
import TaskCard from './TaskCard.vue';
|
||||
|
||||
@@ -31,6 +32,22 @@ const noMore = ref(false);
|
||||
const dialogVisible = ref(false);
|
||||
const currentTask = ref<TaskItem | null>(null);
|
||||
|
||||
// Mobile detection
|
||||
const isMobile = ref(false);
|
||||
|
||||
function checkMobile() {
|
||||
isMobile.value = window.innerWidth < 768;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
checkMobile();
|
||||
window.addEventListener('resize', checkMobile);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', checkMobile);
|
||||
});
|
||||
|
||||
// Mobile filter drawer
|
||||
const showMobileFilter = ref(false);
|
||||
|
||||
@@ -424,12 +441,13 @@ watch(dateRange, () => {
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="图片详情"
|
||||
width="900px"
|
||||
:width="isMobile ? '95%' : '900px'"
|
||||
:fullscreen="isMobile"
|
||||
append-to-body
|
||||
class="image-detail-dialog"
|
||||
align-center
|
||||
>
|
||||
<div v-if="currentTask" class="flex flex-col md:flex-row gap-6 h-[600px]">
|
||||
<div v-if="currentTask" class="flex flex-col md:flex-row gap-4 md:gap-6 h-auto md:h-[600px]">
|
||||
<!-- Left Image -->
|
||||
<div class="flex-1 bg-black/5 rounded-lg flex items-center justify-center overflow-hidden relative group">
|
||||
<el-image
|
||||
@@ -472,7 +490,8 @@ watch(dateRange, () => {
|
||||
</div>
|
||||
|
||||
<!-- Download Button Overlay -->
|
||||
<div v-if="currentTask.storeUrl" class="absolute bottom-4 right-4 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
||||
<div v-if="currentTask.storeUrl" class="absolute bottom-4 right-4 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
||||
<el-button circle type="primary" :icon="ZoomIn" @click="handlePreview(currentTask.storeUrl)" />
|
||||
<el-button circle type="primary" :icon="Download" @click="downloadImage(currentTask.storeUrl)" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -497,6 +516,36 @@ watch(dateRange, () => {
|
||||
</div>
|
||||
|
||||
<div class="mt-auto space-y-3 pt-4 border-t border-gray-100 shrink-0">
|
||||
<!-- 分类标签 -->
|
||||
<div v-if="currentTask.categories && currentTask.categories.length > 0" class="space-y-2">
|
||||
<div class="flex items-center gap-2 text-sm text-gray-500">
|
||||
<el-icon><CollectionTag /></el-icon>
|
||||
<span>分类标签</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<el-tag
|
||||
v-for="tag in currentTask.categories"
|
||||
:key="tag"
|
||||
size="small"
|
||||
type="info"
|
||||
effect="plain"
|
||||
>
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 用户信息 -->
|
||||
<div class="flex justify-between text-sm">
|
||||
<span class="text-gray-500">创建者</span>
|
||||
<span v-if="!currentTask.isAnonymous && currentTask.userName" class="text-blue-500 flex items-center gap-1">
|
||||
<el-icon><User /></el-icon> {{ currentTask.userName }}
|
||||
</span>
|
||||
<span v-else class="text-gray-400 flex items-center gap-1">
|
||||
<el-icon><User /></el-icon> 匿名用户
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between text-sm">
|
||||
<span class="text-gray-500">创建时间</span>
|
||||
<span class="text-gray-800">{{ formatTime(currentTask.creationTime) }}</span>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import type { InputInstance } from 'element-plus';
|
||||
import type { TaskItem, TaskListRequest } from '@/api/aiImage/types';
|
||||
import { Check, CircleCloseFilled, CopyDocument, Download, Filter, Loading, MagicStick, Picture, Refresh, Search, Share, WarningFilled } from '@element-plus/icons-vue';
|
||||
import { Check, CircleCloseFilled, CollectionTag, CopyDocument, Download, Filter, Loading, MagicStick, Picture, Refresh, Search, Share, User, WarningFilled, ZoomIn } from '@element-plus/icons-vue';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { format } from 'date-fns';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
|
||||
import { getMyTasks, publishImage } from '@/api/aiImage';
|
||||
import TaskCard from './TaskCard.vue';
|
||||
|
||||
@@ -19,6 +19,22 @@ const noMore = ref(false);
|
||||
const dialogVisible = ref(false);
|
||||
const currentTask = ref<TaskItem | null>(null);
|
||||
|
||||
// Mobile detection
|
||||
const isMobile = ref(false);
|
||||
|
||||
function checkMobile() {
|
||||
isMobile.value = window.innerWidth < 768;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
checkMobile();
|
||||
window.addEventListener('resize', checkMobile);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', checkMobile);
|
||||
});
|
||||
|
||||
// Mobile filter drawer
|
||||
const showMobileFilter = ref(false);
|
||||
|
||||
@@ -491,12 +507,13 @@ watch([() => searchForm.TaskStatus, () => searchForm.PublishStatus, dateRange],
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="图片详情"
|
||||
width="900px"
|
||||
:width="isMobile ? '95%' : '900px'"
|
||||
:fullscreen="isMobile"
|
||||
append-to-body
|
||||
class="image-detail-dialog"
|
||||
align-center
|
||||
>
|
||||
<div v-if="currentTask" class="flex flex-col md:flex-row gap-6 h-[600px]">
|
||||
<div v-if="currentTask" class="flex flex-col md:flex-row gap-4 md:gap-6 h-auto md:h-[600px]">
|
||||
<!-- Left Image -->
|
||||
<div class="flex-1 bg-black/5 rounded-lg flex items-center justify-center overflow-hidden relative group">
|
||||
<el-image
|
||||
@@ -539,7 +556,8 @@ watch([() => searchForm.TaskStatus, () => searchForm.PublishStatus, dateRange],
|
||||
</div>
|
||||
|
||||
<!-- Download Button Overlay -->
|
||||
<div v-if="currentTask.storeUrl" class="absolute bottom-4 right-4 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
||||
<div v-if="currentTask.storeUrl" class="absolute bottom-4 right-4 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
||||
<el-button circle type="primary" :icon="ZoomIn" @click="handlePreview(currentTask.storeUrl)" />
|
||||
<el-button circle type="primary" :icon="Download" @click="downloadImage(currentTask.storeUrl)" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -564,28 +582,60 @@ watch([() => searchForm.TaskStatus, () => searchForm.PublishStatus, dateRange],
|
||||
</div>
|
||||
|
||||
<div class="mt-auto space-y-3 pt-4 border-t border-gray-100 shrink-0">
|
||||
<!-- 分类标签 -->
|
||||
<div v-if="currentTask.categories && currentTask.categories.length > 0" class="space-y-2">
|
||||
<div class="flex items-center gap-2 text-sm text-gray-500">
|
||||
<el-icon><CollectionTag /></el-icon>
|
||||
<span>分类标签</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
<el-tag
|
||||
v-for="tag in currentTask.categories"
|
||||
:key="tag"
|
||||
size="small"
|
||||
type="info"
|
||||
effect="plain"
|
||||
>
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between text-sm">
|
||||
<span class="text-gray-500">创建时间</span>
|
||||
<span class="text-gray-800">{{ formatTime(currentTask.creationTime) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-sm items-center">
|
||||
<span class="text-gray-500">状态</span>
|
||||
<div class="flex gap-2">
|
||||
<el-tag v-if="currentTask.taskStatus === 'Success'" size="small" type="success">
|
||||
成功
|
||||
</el-tag>
|
||||
<el-tag v-else-if="currentTask.taskStatus === 'Processing'" size="small" type="primary">
|
||||
进行中
|
||||
</el-tag>
|
||||
<el-tag v-else size="small" type="danger">
|
||||
失败
|
||||
</el-tag>
|
||||
|
||||
<el-tag v-if="currentTask.publishStatus === 'Published'" size="small" type="warning" effect="dark">
|
||||
已发布
|
||||
</el-tag>
|
||||
<div class="flex justify-between text-sm items-center">
|
||||
<span class="text-gray-500">任务状态</span>
|
||||
<el-tag v-if="currentTask.taskStatus === 'Success'" size="small" type="success">
|
||||
成功
|
||||
</el-tag>
|
||||
<el-tag v-else-if="currentTask.taskStatus === 'Processing'" size="small" type="primary">
|
||||
进行中
|
||||
</el-tag>
|
||||
<el-tag v-else size="small" type="danger">
|
||||
失败
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 发布状态 -->
|
||||
<div v-if="currentTask.publishStatus === 'Published'" class="flex justify-between text-sm items-center">
|
||||
<span class="text-gray-500">发布状态</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<el-tag size="small" type="warning" effect="dark">已发布</el-tag>
|
||||
<span v-if="!currentTask.isAnonymous && currentTask.userName" class="text-blue-500 text-xs flex items-center gap-1">
|
||||
<el-icon><User /></el-icon> {{ currentTask.userName }}
|
||||
</span>
|
||||
<span v-else class="text-gray-400 text-xs flex items-center gap-1">
|
||||
<el-icon><User /></el-icon> 匿名
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex justify-between text-sm items-center">
|
||||
<span class="text-gray-500">发布状态</span>
|
||||
<el-tag size="small" type="info" effect="plain">未发布</el-tag>
|
||||
</div>
|
||||
|
||||
<div v-if="currentTask.taskStatus === 'Success'" class="pt-2 space-y-2">
|
||||
<div class="grid grid-cols-1 gap-1">
|
||||
|
||||
@@ -103,8 +103,8 @@ async function handleDownload() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Overlay (Hover) -->
|
||||
<div class="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-col items-center justify-center gap-3 z-20 backdrop-blur-[2px]">
|
||||
<!-- Overlay (Hover) - Desktop -->
|
||||
<div class="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-300 hidden md:flex flex-col items-center justify-center gap-3 z-20 backdrop-blur-[2px]">
|
||||
<el-button type="primary" round size="small" class="transform scale-90 group-hover:scale-100 transition-transform shadow-lg" @click.stop="$emit('click')">
|
||||
查看详情
|
||||
</el-button>
|
||||
@@ -128,6 +128,31 @@ async function handleDownload() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Actions Bar -->
|
||||
<div v-if="task.taskStatus === 'Success'" class="absolute bottom-0 left-0 right-0 md:hidden bg-gradient-to-t from-black/70 to-transparent p-2 z-20">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
round
|
||||
class="flex-1"
|
||||
@click.stop="$emit('click')"
|
||||
>
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button circle size="small" :icon="ZoomIn" @click.stop="handlePreview" />
|
||||
<el-button circle size="small" :icon="Download" @click.stop="handleDownload" />
|
||||
<el-button
|
||||
v-if="showPublishStatus && task.publishStatus === 'Unpublished'"
|
||||
circle
|
||||
size="small"
|
||||
type="success"
|
||||
:icon="Share"
|
||||
@click.stop="$emit('publish', task)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status Tag -->
|
||||
<div v-if="showPublishStatus && task.publishStatus === 'Published'" class="absolute top-2 right-2 z-20">
|
||||
<el-tag type="success" effect="dark" size="small" round>
|
||||
|
||||
6
Yi.Ai.Vue3/types/components.d.ts
vendored
6
Yi.Ai.Vue3/types/components.d.ts
vendored
@@ -20,14 +20,10 @@ declare module 'vue' {
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCard: typeof import('element-plus/es')['ElCard']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCheckTag: typeof import('element-plus/es')['ElCheckTag']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||
@@ -46,11 +42,9 @@ declare module 'vue' {
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElSegmented: typeof import('element-plus/es')['ElSegmented']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
|
||||
Reference in New Issue
Block a user