style: 优化资产列表图片自适应展示
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, reactive, ref } from 'vue'
|
||||
import { NAlert, NButton, NFormItem, NImage, NInput, NPopconfirm, NSelect, NTag, NTooltip } from 'naive-ui'
|
||||
import { Eye, Info, RefreshCw, Search, Trash2, Upload } from '@lucide/vue'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { NAlert, NButton, NFormItem, NInput, NPopconfirm, NSelect, NTag, NTooltip } from 'naive-ui'
|
||||
import { RefreshCw, Search, Trash2, Upload } from '@lucide/vue'
|
||||
import { AppDialog, AssetImage, EmptyState } from '../../components/ui'
|
||||
import AppForm from '../../components/ui/AppForm.vue'
|
||||
import WorkspacePage from '../../components/ui/WorkspacePage.vue'
|
||||
import { useQuery } from '../../composables/useQuery'
|
||||
import { fieldRule } from '../../lib/form-rules'
|
||||
import { referenceImageUrl } from '../../lib/assets'
|
||||
import { errorMessage } from '../../lib/http'
|
||||
import { projectsApi } from '../projects/api'
|
||||
import { projectAssetsApi } from './api'
|
||||
@@ -40,8 +39,6 @@ const uploadOpen = ref(false)
|
||||
const editOpen = ref(false)
|
||||
const selectedFile = ref<File | null>(null)
|
||||
const selectedAsset = ref<GlobalProjectAsset | null>(null)
|
||||
const previewAsset = ref<GlobalProjectAsset | null>(null)
|
||||
const previewImage = ref<{ showPreview: () => void } | null>(null)
|
||||
const overflowTitles = reactive(new Set<string>())
|
||||
const uploadForm = reactive({ projectId: '', name: '', category: 'reference' })
|
||||
const editForm = reactive({ name: '', category: '' })
|
||||
@@ -178,13 +175,6 @@ function checkTitleOverflow(assetId: string, event: MouseEvent) {
|
||||
else overflowTitles.delete(assetId)
|
||||
}
|
||||
|
||||
/** 预览按钮独立打开 Naive 图片查看器,不触发卡片详情。 */
|
||||
async function openPreview(asset: GlobalProjectAsset) {
|
||||
previewAsset.value = asset
|
||||
await nextTick()
|
||||
previewImage.value?.showPreview()
|
||||
}
|
||||
|
||||
async function updateAsset() {
|
||||
const asset = selectedAsset.value
|
||||
if (!asset || busy.value) return
|
||||
@@ -311,28 +301,6 @@ async function removeAsset() {
|
||||
>
|
||||
<AssetImage :src="asset.publicUrl" :alt="asset.name" class="asset-library-image" />
|
||||
<div class="asset-library-overlay">
|
||||
<div class="asset-library-actions">
|
||||
<NButton
|
||||
quaternary
|
||||
circle
|
||||
class="asset-overlay-button"
|
||||
aria-label="预览图片"
|
||||
title="预览图片"
|
||||
@click.stop="openPreview(asset)"
|
||||
>
|
||||
<template #icon><Eye :size="16" /></template>
|
||||
</NButton>
|
||||
<NButton
|
||||
quaternary
|
||||
circle
|
||||
class="asset-overlay-button"
|
||||
aria-label="查看素材详情"
|
||||
title="查看素材详情"
|
||||
@click.stop="openEdit(asset)"
|
||||
>
|
||||
<template #icon><Info :size="16" /></template>
|
||||
</NButton>
|
||||
</div>
|
||||
<NTooltip placement="top-start" trigger="hover" :disabled="!overflowTitles.has(asset.id)">
|
||||
<template #trigger>
|
||||
<h2 class="asset-library-title" @mouseenter="checkTitleOverflow(asset.id, $event)">
|
||||
@@ -365,16 +333,6 @@ async function removeAsset() {
|
||||
</EmptyState>
|
||||
<p v-else class="py-10 text-center text-sm text-muted" role="status">正在读取全部资产图片…</p>
|
||||
|
||||
<NImage
|
||||
v-if="previewAsset"
|
||||
ref="previewImage"
|
||||
class="sr-only"
|
||||
:src="referenceImageUrl(previewAsset.publicUrl) ?? undefined"
|
||||
:preview-src="referenceImageUrl(previewAsset.publicUrl) ?? undefined"
|
||||
:alt="previewAsset.name"
|
||||
:previewed-img-props="{ alt: previewAsset.name, referrerpolicy: 'no-referrer' }"
|
||||
/>
|
||||
|
||||
<AppDialog
|
||||
v-model:open="uploadOpen"
|
||||
title="上传资产图片"
|
||||
@@ -485,36 +443,19 @@ async function removeAsset() {
|
||||
box-shadow: inset 0 0 0 2px var(--color-accent);
|
||||
}
|
||||
.asset-library-image {
|
||||
@apply block aspect-auto;
|
||||
@apply block aspect-auto bg-transparent;
|
||||
}
|
||||
.asset-library-image :deep(.reveal-image),
|
||||
.asset-library-image :deep(.n-image),
|
||||
.asset-library-image :deep(.n-image) {
|
||||
@apply block h-auto min-h-0 bg-transparent;
|
||||
}
|
||||
.asset-library-image :deep(.n-image img) {
|
||||
@apply h-auto min-h-[160px];
|
||||
@apply block w-full h-auto min-h-0 object-contain;
|
||||
}
|
||||
.asset-library-overlay {
|
||||
@apply absolute inset-0 flex flex-col justify-end p-[10px] text-white;
|
||||
background: linear-gradient(to top, rgb(0 0 0 / 0.84) 0, rgb(0 0 0 / 0.35) 42%, transparent 70%);
|
||||
}
|
||||
.asset-library-actions {
|
||||
@apply absolute top-[10px] left-[10px] flex items-center gap-1 opacity-0 pointer-events-none;
|
||||
transform: translateY(-4px);
|
||||
transition:
|
||||
opacity 0.18s ease,
|
||||
transform 0.18s ease;
|
||||
}
|
||||
.asset-library-card:hover .asset-library-actions,
|
||||
.asset-library-card:focus-visible .asset-library-actions,
|
||||
.asset-library-card:focus-within .asset-library-actions {
|
||||
@apply opacity-100 pointer-events-auto;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.asset-overlay-button {
|
||||
@apply text-white bg-black/45 backdrop-blur-sm;
|
||||
}
|
||||
.asset-overlay-button:hover {
|
||||
@apply text-white bg-black/60;
|
||||
}
|
||||
.asset-library-title {
|
||||
@apply block w-full truncate text-sm font-semibold;
|
||||
}
|
||||
|
||||
@@ -87,12 +87,9 @@ describe('全局资产库', () => {
|
||||
expect(wrapper.get('.asset-library-card').text()).not.toContain('rain-street-original.png')
|
||||
expect(wrapper.get('.asset-library-card').text()).not.toContain('4.0 KB')
|
||||
|
||||
await wrapper.get('[aria-label="预览图片"]').trigger('click')
|
||||
await flushPromises()
|
||||
expect(document.querySelector('[role="dialog"][aria-label="素材详情与管理"]')).toBeNull()
|
||||
expect(document.querySelector('.n-image-preview-container')).not.toBeNull()
|
||||
|
||||
await wrapper.get('[aria-label="查看素材详情"]').trigger('click')
|
||||
expect(wrapper.find('[aria-label="预览图片"]').exists()).toBe(false)
|
||||
expect(wrapper.find('[aria-label="查看素材详情"]').exists()).toBe(false)
|
||||
await wrapper.get('.asset-library-card').trigger('click')
|
||||
await flushPromises()
|
||||
const dialog = document.querySelector('[role="dialog"][aria-label="素材详情与管理"]')!
|
||||
expect(dialog).not.toBeNull()
|
||||
|
||||
Reference in New Issue
Block a user