feat: 新增永久删除文件接口和前端删除按钮

后端新增 POST api/files/delete 接口,同时删除物理文件、缩略图和数据库记录。
前端在 FileCard 和 FileListItem 中增加 hover 可见的删除按钮,点击后弹出确认提示。
This commit is contained in:
2026-07-09 17:42:27 +08:00
parent 8c92d2fbac
commit 60878f9b6e
11 changed files with 587 additions and 3 deletions
+2
View File
@@ -114,5 +114,7 @@ export const api = {
request('files/played', { method: 'POST', body: { id } }),
saveFileProgress: (id: number, position: number) =>
request('files/progress', { method: 'POST', body: { id, position } }),
deleteFile: (id: number) =>
request('files/delete', { method: 'POST', body: { id } }),
qrCode: () => request<{ url: string; qrCodeBase64: string }>('qrcode'),
}
@@ -272,6 +272,24 @@ function exitSearch() {
searchTotalPages.value = 0
}
async function deleteFile(file: FileRecordDto) {
if (!window.confirm(`确定要永久删除 "${file.fileName}" 吗?\n\n删除后无法恢复!`)) {
return
}
try {
await api.deleteFile(file.id)
await browseDirectory()
if (isSearching.value) {
await doSearch(searchPage.value)
}
if (selectedFile.value?.id === file.id) {
selectedFile.value = null
}
} catch (error) {
setError(error)
}
}
function updatePlaybackPosition(id: number, position: number) {
if (selectedFile.value?.id === id) {
selectedFile.value.playbackPosition = position
@@ -463,7 +481,7 @@ onBeforeUnmount(() => {
<p v-else-if="searchResults.length === 0" class="empty-state">无匹配文件</p>
<div v-else-if="viewMode === 'grid'" class="file-grid">
<template v-for="file in searchResults" :key="file.id">
<FileCard :file="file" show-created-time @select="selectSearchFile" />
<FileCard :file="file" show-created-time @select="selectSearchFile" @delete="deleteFile" />
<SelectedMediaPlayerHost
v-if="selectedFile?.id === file.id"
:ref="setMediaPlayer"
@@ -488,7 +506,7 @@ onBeforeUnmount(() => {
</div>
<div v-else class="file-list">
<template v-for="file in searchResults" :key="file.id">
<FileListItem :file="file" show-created-time @select="selectSearchFile" />
<FileListItem :file="file" show-created-time @select="selectSearchFile" @delete="deleteFile" />
<SelectedMediaPlayerHost
v-if="selectedFile?.id === file.id"
:ref="setMediaPlayer"
@@ -531,7 +549,7 @@ onBeforeUnmount(() => {
</p>
<div v-else-if="viewMode === 'grid'" class="file-grid">
<template v-for="file in recentFiles" :key="file.id">
<FileCard :file="file" :selected="selectedFile?.id === file.id" @select="selectFile" />
<FileCard :file="file" :selected="selectedFile?.id === file.id" @select="selectFile" @delete="deleteFile" />
<SelectedMediaPlayerHost
v-if="selectedFile?.id === file.id"
:ref="setMediaPlayer"
@@ -561,6 +579,7 @@ onBeforeUnmount(() => {
:selected="selectedFile?.id === file.id"
:show-last-played="activeTab === 'recent-played'"
@select="selectFile"
@delete="deleteFile"
/>
<SelectedMediaPlayerHost
v-if="selectedFile?.id === file.id"
@@ -630,6 +649,7 @@ onBeforeUnmount(() => {
:selected="selectedFile?.id === file.id"
show-created-time
@select="selectFile"
@delete="deleteFile"
/>
<SelectedMediaPlayerHost
v-if="selectedFile?.id === file.id"
@@ -661,6 +681,7 @@ onBeforeUnmount(() => {
:selected="selectedFile?.id === file.id"
show-created-time
@select="selectFile"
@delete="deleteFile"
/>
<SelectedMediaPlayerHost
v-if="selectedFile?.id === file.id"
@@ -10,6 +10,7 @@ defineProps<{
defineEmits<{
select: [file: FileRecordDto]
delete: [file: FileRecordDto]
}>()
</script>
@@ -20,6 +21,7 @@ defineEmits<{
type="button"
@click="$emit('select', file)"
>
<span class="delete-btn" @click.stop="$emit('delete', file)" title="永久删除">删除</span>
<img
v-if="file.thumbnailUrl"
:src="api.thumbnailUrl(file.thumbnailUrl)"
@@ -38,3 +40,26 @@ defineEmits<{
</div>
</button>
</template>
<style scoped>
.delete-btn {
position: absolute;
top: 4px;
right: 4px;
padding: 2px 8px;
border-radius: 4px;
background: rgba(0, 0, 0, 0.6);
color: #fff;
font-size: 12px;
line-height: 18px;
cursor: pointer;
opacity: 0;
transition: opacity 0.15s;
}
.file-card:hover .delete-btn {
opacity: 1;
}
.delete-btn:hover {
background: #e53e3e;
}
</style>
@@ -11,6 +11,7 @@ defineProps<{
defineEmits<{
select: [file: FileRecordDto]
delete: [file: FileRecordDto]
}>()
</script>
@@ -38,5 +39,29 @@ defineEmits<{
</small>
<small v-if="showCreatedTime && formatCreatedTime(file)">{{ formatCreatedTime(file) }}</small>
</span>
<span class="delete-btn" @click.stop="$emit('delete', file)" title="永久删除">删除</span>
</button>
</template>
<style scoped>
.delete-btn {
margin-left: auto;
padding: 2px 10px;
border-radius: 4px;
background: rgba(0, 0, 0, 0.06);
color: #999;
font-size: 12px;
line-height: 20px;
cursor: pointer;
flex-shrink: 0;
opacity: 0;
transition: opacity 0.15s;
}
.mobile-file:hover .delete-btn {
opacity: 1;
}
.delete-btn:hover {
background: #e53e3e;
color: #fff;
}
</style>