前端:增加批次管理与旧数据纳入

This commit is contained in:
焦龙言
2026-09-23 16:54:22 +08:00
parent a7dc1a2a1e
commit 8221a5162d
15 changed files with 745 additions and 17 deletions
@@ -0,0 +1,220 @@
<template>
<el-dialog
v-model="visible"
title="纳入测试批次"
width="min(820px, 94vw)"
top="5vh"
append-to-body
destroy-on-close
:close-on-click-modal="!submitting"
:close-on-press-escape="!submitting"
:show-close="!submitting"
:before-close="beforeClose"
@closed="resetState"
>
<el-alert
title="将所选正式数据及其关联闭包纳入测试批次。请先核对完整预览,再输入批次全名确认。"
type="warning"
:closable="false"
show-icon
class="mb8"
/>
<el-alert v-if="errorMessage" :title="errorMessage" type="error" :closable="false" show-icon class="mb8" />
<el-form label-width="100px" @submit.prevent>
<el-form-item label="目标批次">
<el-select v-model="batchId" :loading="batchLoading" :disabled="batchLoading || previewLoading || submitting" placeholder="暂无可用测试批次" @change="handleBatchChange">
<el-option v-for="item in activeBatches" :key="item.batchId" :label="`${item.batchName} (${item.batchCode})`" :value="item.batchId" />
</el-select>
</el-form-item>
<el-form-item label="选择数量">
<span>{{ roots[0]?.resourceIds.length || 0 }} 条 {{ rootType }}</span>
</el-form-item>
</el-form>
<el-skeleton v-if="previewLoading" :rows="5" animated />
<el-empty v-else-if="!preview && !errorMessage && !batchLoading" description="暂无可预览的范围" :image-size="72" />
<template v-else-if="preview">
<el-descriptions :column="2" border size="small" class="mb8">
<el-descriptions-item label="目标批次">{{ preview.batchName }}</el-descriptions-item>
<el-descriptions-item label="影响总数">{{ preview.totalCount }}</el-descriptions-item>
</el-descriptions>
<el-table :data="preview.impacts" size="small" max-height="280" empty-text="无受影响数据">
<el-table-column label="分类" prop="label" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="数量" prop="count" width="88" align="right" />
<el-table-column label="全部编号" min-width="300">
<template #default="scope">
<span class="impact-keys">{{ (scope.row.keys || []).join(', ') || '-' }}</span>
</template>
</el-table-column>
</el-table>
<el-form label-width="100px" class="confirm-form" @submit.prevent>
<el-form-item label="批次全名">
<el-input v-model="batchName" :disabled="submitting" :placeholder="`请输入 ${preview.batchName}`" maxlength="100" />
</el-form-item>
</el-form>
</template>
<template #footer>
<div class="dialog-footer">
<el-button :disabled="submitting" @click="close">取消</el-button>
<el-button type="primary" :loading="previewLoading" :disabled="batchLoading || previewLoading || submitting || !batchId" @click="requestPreview">重新预览</el-button>
<el-button type="danger" :loading="submitting" :disabled="!canExecute" @click="execute">确认纳入</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { listAvailableTestBatches, previewTestBatchAdoption, executeTestBatchAdoption } from '@/api/system/testBatch'
const props = defineProps({
rootType: { type: String, required: true },
resourceIds: { type: Array, required: true }
})
const emit = defineEmits(['success'])
const { proxy } = getCurrentInstance()
const visible = ref(false)
const batchLoading = ref(false)
const previewLoading = ref(false)
const submitting = ref(false)
const errorMessage = ref('')
const activeBatches = ref([])
const batchId = ref(undefined)
const preview = ref(null)
const previewToken = ref('')
const batchName = ref('')
let requestVersion = 0
const roots = computed(() => [{
resourceType: props.rootType,
resourceIds: [...new Set(props.resourceIds.map(Number).filter((id) => Number.isInteger(id) && id > 0))]
}])
const canExecute = computed(() => Boolean(
preview.value && previewToken.value && batchName.value === preview.value.batchName && !previewLoading.value && !submitting.value
))
function invalidatePreview() {
requestVersion += 1
previewLoading.value = false
preview.value = null
previewToken.value = ''
batchName.value = ''
}
function resetState() {
invalidatePreview()
batchLoading.value = false
submitting.value = false
errorMessage.value = ''
activeBatches.value = []
batchId.value = undefined
}
function close() {
if (!submitting.value) visible.value = false
}
function beforeClose(done) {
if (submitting.value) return
done()
}
function normalizePreview(data) {
return {
batchName: data.batchName,
totalCount: data.totalCount,
impacts: Array.isArray(data.impacts) ? data.impacts : []
}
}
function requestPreview() {
if (!batchId.value || !roots.value[0].resourceIds.length || previewLoading.value || submitting.value) {
if (!roots.value[0].resourceIds.length) errorMessage.value = '请至少选择一条有效数据'
return
}
invalidatePreview()
const currentVersion = ++requestVersion
const targetBatchId = batchId.value
previewLoading.value = true
errorMessage.value = ''
previewTestBatchAdoption(targetBatchId, { roots: roots.value }).then((response) => {
if (currentVersion !== requestVersion || targetBatchId !== batchId.value) return
const data = response.data || {}
if (!data.previewToken || !data.batchName) throw new Error('预览响应不完整')
preview.value = normalizePreview(data)
previewToken.value = data.previewToken
}).catch(() => {
if (currentVersion !== requestVersion || targetBatchId !== batchId.value) return
invalidatePreview()
errorMessage.value = '预览已失效或目标批次不可用,请重新预览'
}).finally(() => {
if (currentVersion === requestVersion && targetBatchId === batchId.value) previewLoading.value = false
})
}
function handleBatchChange() {
invalidatePreview()
requestPreview()
}
function loadBatches() {
batchLoading.value = true
errorMessage.value = ''
return listAvailableTestBatches().then((response) => {
activeBatches.value = (Array.isArray(response.data) ? response.data : []).filter((item) => item.status === 'ACTIVE')
batchId.value = activeBatches.value[0]?.batchId
if (!batchId.value) {
errorMessage.value = '暂无可用测试批次'
return
}
requestPreview()
}).catch(() => {
activeBatches.value = []
batchId.value = undefined
errorMessage.value = '测试批次加载失败,请稍后重试'
}).finally(() => {
batchLoading.value = false
})
}
function open() {
if (!roots.value[0].resourceIds.length) {
proxy.$modal.msgWarning('请至少选择一条有效数据')
return
}
resetState()
visible.value = true
loadBatches()
}
function execute() {
if (!preview.value || batchName.value !== preview.value.batchName || !canExecute.value || submitting.value) return
const targetBatchId = batchId.value
const token = previewToken.value
submitting.value = true
errorMessage.value = ''
executeTestBatchAdoption(targetBatchId, { roots: roots.value, batchName: batchName.value, previewToken: previewToken.value }).then((response) => {
proxy.$modal.msgSuccess(response.msg || '数据已纳入测试批次')
visible.value = false
emit('success')
}).catch(() => {
if (targetBatchId !== batchId.value || token !== previewToken.value) return
invalidatePreview()
errorMessage.value = '预览令牌已失效或目标批次已关闭,请重新预览'
}).finally(() => {
submitting.value = false
})
}
watch(() => props.resourceIds, () => {
if (visible.value) invalidatePreview()
}, { deep: true })
defineExpose({ open })
</script>
<style scoped>
.impact-keys { display: block; overflow-wrap: anywhere; white-space: normal; }
.confirm-form { margin-top: 12px; }
</style>