优化页面、修复BUG
This commit is contained in:
@@ -1,86 +1,107 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="show" class="fixed inset-0 bg-black/50 flex items-center justify-center z-50" @click.self="handleClose">
|
||||
<div class="admin-card max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h2 class="text-xl font-serif italic text-white">附件详情</h2>
|
||||
<button @click="handleClose" class="text-white/40 hover:text-white transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="attachment" class="space-y-6">
|
||||
<!-- 图片预览 -->
|
||||
<div v-if="attachment.fileType === 'image'" class="w-full rounded-lg overflow-hidden bg-white/5">
|
||||
<img :src="attachment.fileUrl" :alt="attachment.originalName" class="w-full h-auto max-h-96 object-contain" />
|
||||
<div
|
||||
v-if="show"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4"
|
||||
@click.self="handleClose"
|
||||
>
|
||||
<div class="admin-card max-w-3xl w-full mx-4 max-h-[92vh] overflow-hidden flex flex-col border border-white/10 shadow-2xl">
|
||||
<!-- Header -->
|
||||
<div class="flex items-start justify-between gap-4 px-6 py-4 border-b border-white/10 shrink-0">
|
||||
<div class="min-w-0">
|
||||
<h2 class="text-xl font-serif italic text-white">附件详情</h2>
|
||||
<p v-if="attachment" class="text-sm text-art-muted mt-1 truncate" :title="attachment.originalName">
|
||||
{{ attachment.originalName }}
|
||||
</p>
|
||||
</div>
|
||||
<button @click="handleClose" class="text-white/40 hover:text-white transition-colors shrink-0 p-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 文件信息 -->
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">文件名</label>
|
||||
<p class="text-white">{{ attachment.originalName }}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">文件大小</label>
|
||||
<p class="text-white">{{ formatFileSize(attachment.fileSize) }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">文件类型</label>
|
||||
<p class="text-white">{{ getFileTypeLabel(attachment.fileType) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="attachment.mimeType">
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">MIME类型</label>
|
||||
<p class="text-white font-mono text-sm">{{ attachment.mimeType }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">存储类型</label>
|
||||
<p class="text-white">{{ getStorageTypeLabel(attachment.storageType) }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">分类</label>
|
||||
<CustomSelect
|
||||
v-model="localCategoryId"
|
||||
:options="categoryOptions"
|
||||
placeholder="无分类"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">文件URL</label>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
:value="attachment.fileUrl"
|
||||
readonly
|
||||
class="admin-input flex-1 font-mono text-sm"
|
||||
|
||||
<div v-if="attachment" class="overflow-y-auto custom-scrollbar flex-1">
|
||||
<!-- Media preview -->
|
||||
<div class="px-6 pt-6">
|
||||
<div class="rounded-xl overflow-hidden bg-black/40 border border-white/10 min-h-[200px] flex items-center justify-center">
|
||||
<img
|
||||
v-if="attachment.fileType === 'image'"
|
||||
:src="attachment.fileUrl"
|
||||
:alt="attachment.originalName"
|
||||
class="w-full max-h-[420px] object-contain"
|
||||
/>
|
||||
<button
|
||||
@click="handleCopyUrl"
|
||||
class="admin-btn-secondary px-4 whitespace-nowrap"
|
||||
>
|
||||
复制
|
||||
</button>
|
||||
<video
|
||||
v-else-if="attachment.fileType === 'video'"
|
||||
:src="attachment.fileUrl"
|
||||
controls
|
||||
preload="metadata"
|
||||
class="w-full max-h-[420px] bg-black"
|
||||
/>
|
||||
<div v-else class="flex flex-col items-center justify-center py-16 px-6 text-center">
|
||||
<div class="w-20 h-20 rounded-2xl bg-white/5 border border-white/10 flex items-center justify-center mb-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="text-art-muted">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||
<polyline points="14 2 14 8 20 8"></polyline>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="text-white font-medium mb-1">{{ attachment.originalName }}</p>
|
||||
<p class="text-sm text-art-muted">此文件类型暂不支持预览</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="attachment.createdAt" class="text-xs text-art-muted">
|
||||
创建时间: {{ formatDate(attachment.createdAt) }}
|
||||
|
||||
<!-- Badges -->
|
||||
<div class="px-6 pt-4 flex flex-wrap gap-2">
|
||||
<span class="px-3 py-1 rounded-full text-xs font-medium border" :class="typeBadgeClass">
|
||||
{{ getFileTypeLabel(attachment.fileType) }}
|
||||
</span>
|
||||
<span class="px-3 py-1 rounded-full text-xs bg-white/5 text-art-muted border border-white/10">
|
||||
{{ formatFileSize(attachment.fileSize) }}
|
||||
</span>
|
||||
<span class="px-3 py-1 rounded-full text-xs bg-white/5 text-art-muted border border-white/10">
|
||||
{{ getStorageTypeLabel(attachment.storageType) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Meta grid -->
|
||||
<div class="px-6 py-5 grid sm:grid-cols-2 gap-4">
|
||||
<div class="rounded-lg bg-white/[0.03] border border-white/5 p-4">
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">MIME 类型</label>
|
||||
<p class="text-white font-mono text-sm break-all">{{ attachment.mimeType || '—' }}</p>
|
||||
</div>
|
||||
<div class="rounded-lg bg-white/[0.03] border border-white/5 p-4">
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">创建时间</label>
|
||||
<p class="text-white text-sm">{{ attachment.createdAt ? formatDate(attachment.createdAt) : '—' }}</p>
|
||||
</div>
|
||||
<div class="sm:col-span-2 rounded-lg bg-white/[0.03] border border-white/5 p-4">
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">分类</label>
|
||||
<CustomSelect
|
||||
v-model="localCategoryId"
|
||||
:options="categoryOptions"
|
||||
placeholder="无分类"
|
||||
/>
|
||||
</div>
|
||||
<div class="sm:col-span-2 rounded-lg bg-white/[0.03] border border-white/5 p-4">
|
||||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider mb-2">文件 URL</label>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
:value="attachment.fileUrl"
|
||||
readonly
|
||||
class="admin-input flex-1 font-mono text-sm"
|
||||
/>
|
||||
<button @click="handleCopyUrl" class="admin-btn-secondary px-4 whitespace-nowrap">
|
||||
复制
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="flex gap-3 pt-4 border-t border-white/10">
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="flex gap-3 px-6 py-4 border-t border-white/10 shrink-0">
|
||||
<button @click="handleClose" class="admin-btn-secondary flex-1">
|
||||
取消
|
||||
</button>
|
||||
@@ -90,7 +111,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
@@ -132,7 +152,6 @@ const emit = defineEmits<{
|
||||
const localCategoryId = ref<number>(0)
|
||||
const saving = ref(false)
|
||||
|
||||
// 计算属性:分类选项
|
||||
const categoryOptions = computed(() => {
|
||||
return [
|
||||
{ value: 0, label: '无分类' },
|
||||
@@ -140,7 +159,16 @@ const categoryOptions = computed(() => {
|
||||
]
|
||||
})
|
||||
|
||||
// 监听attachment变化,更新本地分类ID
|
||||
const typeBadgeClass = computed(() => {
|
||||
const map: Record<string, string> = {
|
||||
image: 'bg-emerald-500/10 text-emerald-300 border-emerald-500/30',
|
||||
video: 'bg-violet-500/10 text-violet-300 border-violet-500/30',
|
||||
document: 'bg-blue-500/10 text-blue-300 border-blue-500/30',
|
||||
other: 'bg-white/5 text-art-muted border-white/10'
|
||||
}
|
||||
return map[props.attachment?.fileType || 'other'] || map.other
|
||||
})
|
||||
|
||||
watch(() => props.attachment, (newAttachment) => {
|
||||
if (newAttachment) {
|
||||
localCategoryId.value = newAttachment.categoryId || 0
|
||||
@@ -155,7 +183,6 @@ const handleSave = () => {
|
||||
saving.value = true
|
||||
const categoryId = localCategoryId.value === 0 ? null : localCategoryId.value
|
||||
emit('save', { categoryId })
|
||||
// saving状态由父组件控制
|
||||
}
|
||||
|
||||
const handleCopyUrl = () => {
|
||||
@@ -183,8 +210,8 @@ const getFileTypeLabel = (fileType: string): string => {
|
||||
const getStorageTypeLabel = (storageType?: string): string => {
|
||||
const labels: Record<string, string> = {
|
||||
local: '本地存储',
|
||||
qcloud: '腾讯云COS',
|
||||
aliyun: '阿里云OSS',
|
||||
qcloud: '腾讯云 COS',
|
||||
aliyun: '阿里云 OSS',
|
||||
qiniu: '七牛云'
|
||||
}
|
||||
return labels[storageType || 'local'] || storageType || '本地存储'
|
||||
@@ -195,12 +222,15 @@ const formatDate = (timestamp: string | number): string => {
|
||||
return date.toLocaleString('zh-CN')
|
||||
}
|
||||
|
||||
// 暴露saving状态给父组件
|
||||
defineExpose({
|
||||
saving
|
||||
})
|
||||
defineExpose({ saving })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 使用全局admin样式,无需额外样式 */
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user