修复
This commit is contained in:
@@ -164,6 +164,11 @@
|
||||
<div class="material-item__overlay">
|
||||
<span class="material-item__type-tag">{{ getFileTypeText(file.fileName) }}</span>
|
||||
</div>
|
||||
<!-- hover 播放按钮 -->
|
||||
<div class="material-item__play-overlay" @click.stop="handlePreview(file)">
|
||||
<PlayCircleOutlined />
|
||||
<span v-if="file.duration" class="duration-tag">{{ formatDuration(file.duration) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件信息 -->
|
||||
@@ -261,6 +266,13 @@
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- 视频预览弹窗 -->
|
||||
<VideoPreviewModal
|
||||
v-model:open="previewVisible"
|
||||
:video-url="previewUrl"
|
||||
:title="previewTitle"
|
||||
/>
|
||||
</div>
|
||||
</FullWidthLayout>
|
||||
</template>
|
||||
@@ -279,13 +291,15 @@ import {
|
||||
VideoCameraOutlined,
|
||||
UserOutlined,
|
||||
CheckOutlined,
|
||||
CheckSquareOutlined
|
||||
CheckSquareOutlined,
|
||||
PlayCircleOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import MaterialUploadModal from '@/components/material/MaterialUploadModal.vue';
|
||||
import MaterialService, { MaterialGroupService } from '@/api/material';
|
||||
import FullWidthLayout from '@/layouts/components/FullWidthLayout.vue';
|
||||
import { useUserStore } from '@/stores/user';
|
||||
import VideoPreviewModal from '@/components/VideoPreviewModal.vue';
|
||||
|
||||
// 用户状态(获取存储配额)
|
||||
const userStore = useUserStore()
|
||||
@@ -313,6 +327,11 @@ const createGroupForm = reactive({
|
||||
// 文件选择
|
||||
const selectedFileIds = ref([])
|
||||
|
||||
// 视频预览状态
|
||||
const previewVisible = ref(false)
|
||||
const previewUrl = ref('')
|
||||
const previewTitle = ref('')
|
||||
|
||||
// 编辑状态
|
||||
const editingFileId = ref(null)
|
||||
const editingDisplayName = ref('')
|
||||
@@ -398,6 +417,22 @@ const handleSaveGroupName = async (group) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 通用的确认删除弹窗
|
||||
const showConfirmDelete = (title, content) => {
|
||||
return new Promise((resolve) => {
|
||||
Modal.confirm({
|
||||
title,
|
||||
content,
|
||||
okText: '确认删除',
|
||||
cancelText: '取消',
|
||||
okType: 'danger',
|
||||
centered: true,
|
||||
onOk: () => resolve(true),
|
||||
onCancel: () => resolve(false),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const handleDeleteGroup = async (group, event) => {
|
||||
event?.stopPropagation?.()
|
||||
|
||||
@@ -407,39 +442,17 @@ const handleDeleteGroup = async (group, event) => {
|
||||
return
|
||||
}
|
||||
|
||||
const confirmed = await new Promise((resolve) => {
|
||||
Modal.confirm({
|
||||
title: '删除分组',
|
||||
content: `确定要删除分组「${group.name}」吗?此操作不可恢复。`,
|
||||
okText: '确认删除',
|
||||
cancelText: '取消',
|
||||
okType: 'danger',
|
||||
centered: true,
|
||||
class: 'delete-group-modal',
|
||||
onOk() {
|
||||
resolve(true)
|
||||
},
|
||||
onCancel() {
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const confirmed = await showConfirmDelete('删除分组', `确定要删除分组「${group.name}」吗?此操作不可恢复。`)
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
await MaterialGroupService.deleteGroup(group.id)
|
||||
|
||||
// 移除已删除的分组
|
||||
const index = groupList.value.findIndex(g => g.id === group.id)
|
||||
if (index > -1) {
|
||||
groupList.value.splice(index, 1)
|
||||
}
|
||||
|
||||
// 重新选择分组
|
||||
// 移除已删除的分组并重新选择
|
||||
groupList.value = groupList.value.filter(g => g.id !== group.id)
|
||||
if (selectedGroupId.value === group.id) {
|
||||
selectedGroupId.value = groupList.value.length > 0 ? groupList.value[0].id : null
|
||||
selectedGroupId.value = groupList.value[0]?.id ?? null
|
||||
await loadFileList()
|
||||
}
|
||||
|
||||
@@ -582,34 +595,16 @@ const handleBatchDelete = async () => {
|
||||
|
||||
const count = selectedFileIds.value.length
|
||||
const fileIdsToDelete = [...selectedFileIds.value]
|
||||
const confirmed = await new Promise((resolve) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除选中的 ${count} 个文件吗?此操作不可恢复。`,
|
||||
okText: '确认删除',
|
||||
cancelText: '取消',
|
||||
okType: 'danger',
|
||||
centered: true,
|
||||
class: 'batch-delete-modal',
|
||||
onOk() {
|
||||
resolve(true)
|
||||
},
|
||||
onCancel() {
|
||||
resolve(false)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const confirmed = await showConfirmDelete('确认删除', `确定要删除选中的 ${count} 个文件吗?此操作不可恢复。`)
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
await MaterialService.deleteFiles(fileIdsToDelete)
|
||||
|
||||
// 移除已删除的文件
|
||||
// 移除已删除的文件并更新状态
|
||||
fileList.value = fileList.value.filter(file => !fileIdsToDelete.includes(file.id))
|
||||
|
||||
// 更新状态
|
||||
totalFileCount.value = Math.max(0, totalFileCount.value - count)
|
||||
selectedFileIds.value = []
|
||||
|
||||
@@ -618,7 +613,7 @@ const handleBatchDelete = async () => {
|
||||
|
||||
// 如果删除后当前页没有数据了,则加载上一页
|
||||
if (fileList.value.length === 0 && pagination.current > 1) {
|
||||
pagination.current = pagination.current - 1
|
||||
pagination.current--
|
||||
await loadFileList()
|
||||
}
|
||||
|
||||
@@ -631,14 +626,12 @@ const handleBatchDelete = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const formatFileSize = (size) => {
|
||||
const formatFileSize = (bytes) => {
|
||||
if (!bytes) return '0 B'
|
||||
const units = ['B', 'KB', 'MB', 'GB']
|
||||
let unitIndex = 0
|
||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
||||
size /= 1024
|
||||
unitIndex++
|
||||
}
|
||||
return `${size.toFixed(2)} ${units[unitIndex]}`
|
||||
const index = Math.floor(Math.log(bytes) / Math.log(1024))
|
||||
const size = bytes / Math.pow(1024, index)
|
||||
return `${size.toFixed(1)} ${units[index]}`
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
@@ -707,6 +700,43 @@ const handleImageError = (e) => {
|
||||
e.target.style.display = 'none'
|
||||
}
|
||||
|
||||
// 视频预览
|
||||
const handlePreview = async (file) => {
|
||||
if (!file.id) {
|
||||
message.warning('文件信息不完整')
|
||||
return
|
||||
}
|
||||
|
||||
// 显示加载提示
|
||||
const hideLoading = message.loading('正在获取视频地址...', 0)
|
||||
|
||||
try {
|
||||
// 调用后端API获取带签名的视频播放URL
|
||||
const res = await MaterialService.getVideoPlayUrl(file.id)
|
||||
hideLoading()
|
||||
|
||||
if (res.code === 0 && res.data) {
|
||||
previewUrl.value = res.data
|
||||
previewTitle.value = file.displayName || file.fileName || '视频预览'
|
||||
previewVisible.value = true
|
||||
} else {
|
||||
message.error(res.msg || '获取视频地址失败')
|
||||
}
|
||||
} catch (error) {
|
||||
hideLoading()
|
||||
console.error('获取视频播放URL失败:', error)
|
||||
message.error('获取视频地址失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化时长
|
||||
const formatDuration = (seconds) => {
|
||||
if (!seconds) return ''
|
||||
const mins = Math.floor(seconds / 60)
|
||||
const secs = Math.floor(seconds % 60)
|
||||
return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
// 监听分类变化
|
||||
watch(activeCategory, () => {
|
||||
selectedFileIds.value = []
|
||||
@@ -1251,6 +1281,37 @@ onMounted(async () => {
|
||||
transition: opacity var(--duration-slow) ease;
|
||||
}
|
||||
|
||||
&__play-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--space-2);
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
opacity: 0;
|
||||
transition: opacity var(--duration-base);
|
||||
cursor: pointer;
|
||||
|
||||
.anticon {
|
||||
font-size: 48px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.duration-tag {
|
||||
padding: 2px 8px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
}
|
||||
|
||||
&__preview:hover &__play-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&__type-tag {
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: 600;
|
||||
@@ -1358,8 +1419,7 @@ onMounted(async () => {
|
||||
// ========================================
|
||||
// 弹窗样式
|
||||
// ========================================
|
||||
:deep(.batch-delete-modal),
|
||||
:deep(.delete-group-modal) {
|
||||
:deep(.ant-modal-confirm) {
|
||||
.ant-modal-content {
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
|
||||
Reference in New Issue
Block a user