feat: 数据集版本管理支持删除历史版本
DatasetVersionList 新增 next_version_number,API 与 Mock 补充删除版本接口与并发控制,DatasetVersionBar 增加删除操作并对初始版本与最后版本禁用,DatasetPreviewView 接入删除流程,回归脚本与设计走查同步。
This commit is contained in:
@@ -2,10 +2,12 @@
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { onBeforeRouteLeave, useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import AppConfirmDialog from '@/components/AppConfirmDialog.vue'
|
||||
import PageCard from '@/components/PageCard.vue'
|
||||
import {
|
||||
activateDatasetFileVersion,
|
||||
createDatasetFileVersion,
|
||||
deleteDatasetFileVersion,
|
||||
downloadFileUrl,
|
||||
getDataset,
|
||||
getDatasetFileVersionContent,
|
||||
@@ -23,6 +25,7 @@ import type { DatasetFile, DatasetItem, DatasetVersion } from '@/types'
|
||||
|
||||
const route = useRoute()
|
||||
const datasetId = route.params.id as string
|
||||
const confirmDialogRef = ref<InstanceType<typeof AppConfirmDialog>>()
|
||||
|
||||
const loading = ref(false)
|
||||
const previewLoading = ref(false)
|
||||
@@ -44,6 +47,8 @@ const activeVersionId = ref('')
|
||||
const loadedVersionId = ref('')
|
||||
const versionLoading = ref(false)
|
||||
const activatingVersion = ref(false)
|
||||
const deletingVersion = ref(false)
|
||||
const nextVersionNumber = ref(1)
|
||||
let versionRequestId = 0
|
||||
|
||||
const files = computed<DatasetFile[]>(() => dataset.value?.files || [])
|
||||
@@ -54,7 +59,6 @@ const isRecordFile = computed(() => recordResult.value.supported)
|
||||
const records = computed(() => recordResult.value.records)
|
||||
const viewedVersion = computed(() => versions.value.find((item) => item.id === loadedVersionId.value))
|
||||
const isViewingActiveVersion = computed(() => Boolean(loadedVersionId.value) && loadedVersionId.value === activeVersionId.value)
|
||||
const nextVersionNumber = computed(() => Math.max(0, ...versions.value.map((item) => item.version)) + 1)
|
||||
const currentDraft = computed(() => editingRecord.value?.kind === 'object'
|
||||
? JSON.stringify(editFields.value)
|
||||
: rawDraft.value)
|
||||
@@ -100,6 +104,8 @@ async function loadVersions(file: DatasetFile) {
|
||||
const activeContent = await getDatasetFileVersionContent(fileKey(file), result.active_version_id)
|
||||
if (requestId !== versionRequestId) return
|
||||
versions.value = result.versions
|
||||
nextVersionNumber.value = result.next_version_number
|
||||
?? Math.max(0, ...result.versions.map((item) => item.version)) + 1
|
||||
activeVersionId.value = result.active_version_id
|
||||
selectedVersionId.value = result.active_version_id
|
||||
loadedVersionId.value = result.active_version_id
|
||||
@@ -169,6 +175,52 @@ async function activateViewedVersion() {
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteViewedVersion() {
|
||||
if (
|
||||
!selectedFile.value
|
||||
|| !viewedVersion.value
|
||||
|| isViewingActiveVersion.value
|
||||
|| deletingVersion.value
|
||||
) return
|
||||
|
||||
const targetVersion = viewedVersion.value
|
||||
const confirmed = await confirmDialogRef.value?.open({
|
||||
title: `删除 V${targetVersion.version} 版本?`,
|
||||
message: `删除后无法恢复。当前版本 V${versions.value.find((item) => item.id === activeVersionId.value)?.version || '-'} 不受影响。`,
|
||||
confirmText: '删除版本',
|
||||
cancelText: '取消',
|
||||
tone: 'danger',
|
||||
})
|
||||
if (!confirmed) return
|
||||
|
||||
deletingVersion.value = true
|
||||
versionLoading.value = true
|
||||
try {
|
||||
const result = await deleteDatasetFileVersion(
|
||||
fileKey(selectedFile.value),
|
||||
targetVersion.id,
|
||||
activeVersionId.value,
|
||||
)
|
||||
const activeContent = await getDatasetFileVersionContent(
|
||||
fileKey(selectedFile.value),
|
||||
result.active_version_id,
|
||||
)
|
||||
versions.value = result.versions
|
||||
nextVersionNumber.value = result.next_version_number
|
||||
?? Math.max(0, ...result.versions.map((item) => item.version)) + 1
|
||||
activeVersionId.value = result.active_version_id
|
||||
selectedVersionId.value = result.active_version_id
|
||||
loadedVersionId.value = result.active_version_id
|
||||
previewContent.value = activeContent.content
|
||||
baseVersionContent.value = activeContent.content
|
||||
resetRecordEditor()
|
||||
ElMessage.success(`V${targetVersion.version} 版本已删除`)
|
||||
} finally {
|
||||
versionLoading.value = false
|
||||
deletingVersion.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openRecordEditor(record: DatasetRecord) {
|
||||
if (!isViewingActiveVersion.value) {
|
||||
ElMessage.warning('历史版本为只读,请先设为当前版本')
|
||||
@@ -296,6 +348,7 @@ async function saveVersion() {
|
||||
previewContent.value = result.content
|
||||
baseVersionContent.value = result.content
|
||||
versions.value = [result.version, ...versions.value]
|
||||
nextVersionNumber.value = result.version.version + 1
|
||||
activeVersionId.value = result.version.id
|
||||
selectedVersionId.value = result.version.id
|
||||
loadedVersionId.value = result.version.id
|
||||
@@ -357,8 +410,10 @@ onBeforeUnmount(() => window.removeEventListener('beforeunload', handleBeforeUnl
|
||||
:version-loading="versionLoading"
|
||||
:saving-record="savingRecord"
|
||||
:activating-version="activatingVersion"
|
||||
:deleting-version="deletingVersion"
|
||||
@change="handleViewedVersionChange"
|
||||
@activate="activateViewedVersion"
|
||||
@delete="deleteViewedVersion"
|
||||
/>
|
||||
|
||||
<el-alert
|
||||
@@ -445,6 +500,8 @@ onBeforeUnmount(() => window.removeEventListener('beforeunload', handleBeforeUnl
|
||||
@update:field="updateEditField"
|
||||
@update:raw-draft="rawDraft = $event"
|
||||
/>
|
||||
|
||||
<AppConfirmDialog ref="confirmDialogRef" />
|
||||
</PageCard>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { DatasetVersion } from '@/types'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -11,14 +12,36 @@ const props = defineProps<{
|
||||
versionLoading: boolean
|
||||
savingRecord: boolean
|
||||
activatingVersion: boolean
|
||||
deletingVersion: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
change: [versionId: string]
|
||||
activate: []
|
||||
delete: []
|
||||
}>()
|
||||
|
||||
type VersionAction = 'activate' | 'delete'
|
||||
|
||||
const actionsDisabled = computed(
|
||||
() =>
|
||||
props.versionLoading ||
|
||||
props.savingRecord ||
|
||||
props.activatingVersion ||
|
||||
props.deletingVersion,
|
||||
)
|
||||
|
||||
const deleteDisabledReason = computed(() => {
|
||||
if (props.viewedVersion?.version === 1) return '初始版本不可删除'
|
||||
if (props.versions.length <= 1) return '至少需要保留一个版本'
|
||||
return ''
|
||||
})
|
||||
|
||||
const deleteDisabled = computed(
|
||||
() => actionsDisabled.value || Boolean(deleteDisabledReason.value),
|
||||
)
|
||||
|
||||
function formatVersionTime(value: string) {
|
||||
return new Date(value).toLocaleString('zh-CN', { hour12: false })
|
||||
}
|
||||
@@ -32,6 +55,16 @@ function handleVersionChange(versionId: string) {
|
||||
emit('update:modelValue', versionId)
|
||||
emit('change', versionId)
|
||||
}
|
||||
|
||||
function handleVersionAction(command: VersionAction) {
|
||||
if (actionsDisabled.value) return
|
||||
if (command === 'activate') {
|
||||
emit('activate')
|
||||
return
|
||||
}
|
||||
if (deleteDisabled.value) return
|
||||
emit('delete')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -70,15 +103,51 @@ function handleVersionChange(versionId: string) {
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<el-button
|
||||
<el-dropdown
|
||||
v-if="viewedVersion && !isViewingActiveVersion"
|
||||
type="primary"
|
||||
:loading="activatingVersion"
|
||||
@click="emit('activate')"
|
||||
class="version-actions"
|
||||
trigger="click"
|
||||
placement="bottom-end"
|
||||
popper-class="version-actions-popper"
|
||||
:disabled="actionsDisabled"
|
||||
@command="handleVersionAction"
|
||||
>
|
||||
<i class="fa fa-check-circle" />
|
||||
设为当前版本
|
||||
</el-button>
|
||||
<el-button
|
||||
class="version-actions-button"
|
||||
:loading="activatingVersion || deletingVersion"
|
||||
:disabled="actionsDisabled"
|
||||
aria-label="打开历史版本操作菜单"
|
||||
>
|
||||
<i
|
||||
v-if="!activatingVersion && !deletingVersion"
|
||||
class="fa fa-ellipsis-h"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</el-button>
|
||||
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
command="activate"
|
||||
class="version-action-primary"
|
||||
:disabled="actionsDisabled"
|
||||
>
|
||||
<i class="fa fa-check-circle" aria-hidden="true" />
|
||||
设为当前版本
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
command="delete"
|
||||
class="version-action-danger"
|
||||
:disabled="deleteDisabled"
|
||||
:title="deleteDisabledReason || undefined"
|
||||
divided
|
||||
>
|
||||
<i class="fa fa-trash-o" aria-hidden="true" />
|
||||
删除版本
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -129,13 +198,43 @@ function handleVersionChange(versionId: string) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.version-control-bar > .el-button {
|
||||
min-height: 40px;
|
||||
.version-actions {
|
||||
flex: 0 0 auto;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.version-control-bar > .el-button i {
|
||||
margin-right: 6px;
|
||||
.version-actions-button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex: 0 0 auto;
|
||||
padding: 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.version-actions-button i {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
:global(.version-actions-popper .el-dropdown-menu__item) {
|
||||
gap: 8px;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
:global(.version-actions-popper .el-dropdown-menu) {
|
||||
min-width: 168px;
|
||||
}
|
||||
|
||||
:global(.version-actions-popper .version-action-primary:not(.is-disabled) i) {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
:global(.version-actions-popper .version-action-danger:not(.is-disabled)) {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
|
||||
:global(.version-actions-popper .version-action-danger:not(.is-disabled):hover) {
|
||||
color: var(--el-color-danger);
|
||||
background: var(--el-color-danger-light-9);
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
@@ -147,8 +246,22 @@ function handleVersionChange(versionId: string) {
|
||||
}
|
||||
|
||||
.version-select,
|
||||
.version-control-bar > .el-button {
|
||||
.version-selector-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.version-status {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.version-status > span {
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.version-actions {
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user