fix: 支持关闭自动导入状态提示
This commit is contained in:
@@ -116,6 +116,7 @@
|
||||
"pendingCount": "Pending",
|
||||
"skippedCount": "Skipped",
|
||||
"continueBatch": "Continue",
|
||||
"dismissBatchStatus": "Dismiss automatic import status",
|
||||
"batchStatusLoadFailed": "Unable to load the automatic import status. Retrying automatically.",
|
||||
"interruptFallback": "Automatic import was interrupted. Resume after the service recovers.",
|
||||
"interruptCode": "Error code",
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
"pendingCount": "等待",
|
||||
"skippedCount": "跳过",
|
||||
"continueBatch": "继续",
|
||||
"dismissBatchStatus": "关闭自动导入状态提示",
|
||||
"batchStatusLoadFailed": "自动导入状态暂时无法获取,将自动重试",
|
||||
"interruptFallback": "自动导入发生异常,批次已中断,请确认服务恢复后继续",
|
||||
"interruptCode": "错误标识",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { flushPromises, mount } from '@vue/test-utils';
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import DocumentImportBatchStatus from './DocumentImportBatchStatus.vue';
|
||||
|
||||
@@ -31,6 +31,10 @@ function createBatch(status: 'COMPLETED' | 'RUNNING', completedCount: number) {
|
||||
}
|
||||
|
||||
describe('documentImportBatchStatus', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.useRealTimers();
|
||||
@@ -77,6 +81,65 @@ describe('documentImportBatchStatus', () => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('关闭当前批次提示后停止轮询且不影响导入接口', async () => {
|
||||
vi.useFakeTimers();
|
||||
apiMocks.get.mockResolvedValue({
|
||||
data: createBatch('RUNNING', 1),
|
||||
errorCode: 0,
|
||||
});
|
||||
|
||||
const wrapper = mount(DocumentImportBatchStatus, {
|
||||
props: { knowledgeId: 'knowledge-1' },
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
const closeButton = wrapper.find('.batch-status__close');
|
||||
expect(closeButton.attributes('aria-label')).toBe(
|
||||
'documentCollection.importDoc.dismissBatchStatus',
|
||||
);
|
||||
await closeButton.trigger('click');
|
||||
|
||||
expect(wrapper.find('.batch-status').exists()).toBe(false);
|
||||
await vi.advanceTimersByTimeAsync(30_000);
|
||||
expect(apiMocks.get).toHaveBeenCalledTimes(1);
|
||||
expect(apiMocks.post).not.toHaveBeenCalled();
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('当前批次关闭状态在本地保留且新批次仍会展示', async () => {
|
||||
apiMocks.get
|
||||
.mockResolvedValueOnce({
|
||||
data: createBatch('RUNNING', 1),
|
||||
errorCode: 0,
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
data: createBatch('RUNNING', 1),
|
||||
errorCode: 0,
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
data: { ...createBatch('RUNNING', 0), batchId: 'batch-2' },
|
||||
errorCode: 0,
|
||||
});
|
||||
|
||||
const firstWrapper = mount(DocumentImportBatchStatus, {
|
||||
props: { knowledgeId: 'knowledge-1' },
|
||||
});
|
||||
await flushPromises();
|
||||
await firstWrapper.find('.batch-status__close').trigger('click');
|
||||
firstWrapper.unmount();
|
||||
|
||||
const secondWrapper = mount(DocumentImportBatchStatus, {
|
||||
props: { knowledgeId: 'knowledge-1' },
|
||||
});
|
||||
await flushPromises();
|
||||
expect(secondWrapper.find('.batch-status').exists()).toBe(false);
|
||||
|
||||
await secondWrapper.setProps({ refreshKey: 1 });
|
||||
await flushPromises();
|
||||
expect(secondWrapper.find('.batch-status').exists()).toBe(true);
|
||||
secondWrapper.unmount();
|
||||
});
|
||||
|
||||
it('失败批次关联任务实际完成后不再展示提示', async () => {
|
||||
apiMocks.get.mockResolvedValue({
|
||||
data: {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { $t } from '@easyflow/locales';
|
||||
|
||||
import { Close } from '@element-plus/icons-vue';
|
||||
import { ElAlert, ElButton, ElProgress } from 'element-plus';
|
||||
|
||||
import { api } from '#/api/request';
|
||||
@@ -48,6 +49,7 @@ const props = defineProps({
|
||||
const emit = defineEmits(['continued']);
|
||||
const batch = ref<BatchStatus>();
|
||||
const continuing = ref(false);
|
||||
const dismissedBatchId = ref('');
|
||||
const loadError = ref('');
|
||||
const refreshing = ref(false);
|
||||
let pollTimer: null | ReturnType<typeof setTimeout> = null;
|
||||
@@ -55,6 +57,14 @@ let disposed = false;
|
||||
let refreshGeneration = 0;
|
||||
let pollDelayMs = 3000;
|
||||
const TERMINAL_RECHECK_DELAY_MS = 15_000;
|
||||
const DISMISSED_BATCH_STORAGE_PREFIX =
|
||||
'easyflow.documentImport.dismissedBatch.';
|
||||
|
||||
const batchDismissed = computed(
|
||||
() =>
|
||||
Boolean(batch.value?.batchId) &&
|
||||
batch.value?.batchId === dismissedBatchId.value,
|
||||
);
|
||||
|
||||
const canContinue = computed(
|
||||
() =>
|
||||
@@ -124,6 +134,35 @@ const interruptMetadata = computed(() => {
|
||||
return details.join(' · ');
|
||||
});
|
||||
|
||||
function dismissalStorageKey(knowledgeId: string) {
|
||||
return `${DISMISSED_BATCH_STORAGE_PREFIX}${knowledgeId}`;
|
||||
}
|
||||
|
||||
function readDismissedBatchId(knowledgeId: string) {
|
||||
if (!knowledgeId || typeof window === 'undefined') {
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
return (
|
||||
window.localStorage.getItem(dismissalStorageKey(knowledgeId))?.trim() ||
|
||||
''
|
||||
);
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function persistDismissedBatchId(knowledgeId: string, batchId: string) {
|
||||
if (!knowledgeId || !batchId || typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
window.localStorage.setItem(dismissalStorageKey(knowledgeId), batchId);
|
||||
} catch {
|
||||
// 浏览器禁用存储时,关闭操作在当前组件生命周期内仍然有效。
|
||||
}
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
if (!props.knowledgeId) return;
|
||||
const currentGeneration = ++refreshGeneration;
|
||||
@@ -173,6 +212,9 @@ function schedulePoll() {
|
||||
pollTimer = setTimeout(refresh, retryDelay);
|
||||
return;
|
||||
}
|
||||
if (batchDismissed.value) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!batch.value ||
|
||||
batch.value.status === 'COMPLETED' ||
|
||||
@@ -188,6 +230,18 @@ function schedulePoll() {
|
||||
pollTimer = setTimeout(refresh, delay);
|
||||
}
|
||||
|
||||
function dismissBatchStatus() {
|
||||
if (!batch.value?.batchId) {
|
||||
return;
|
||||
}
|
||||
dismissedBatchId.value = batch.value.batchId;
|
||||
persistDismissedBatchId(props.knowledgeId, batch.value.batchId);
|
||||
loadError.value = '';
|
||||
refreshGeneration += 1;
|
||||
if (pollTimer) clearTimeout(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
|
||||
async function continueBatch() {
|
||||
if (!batch.value || continuing.value) return;
|
||||
continuing.value = true;
|
||||
@@ -208,6 +262,7 @@ async function continueBatch() {
|
||||
|
||||
onMounted(() => {
|
||||
disposed = false;
|
||||
dismissedBatchId.value = readDismissedBatchId(props.knowledgeId);
|
||||
refresh();
|
||||
});
|
||||
|
||||
@@ -223,6 +278,7 @@ watch(
|
||||
const knowledgeChanged = knowledgeId !== previousKnowledgeId;
|
||||
if (knowledgeChanged) {
|
||||
batch.value = undefined;
|
||||
dismissedBatchId.value = readDismissedBatchId(knowledgeId);
|
||||
loadError.value = '';
|
||||
pollDelayMs = 3000;
|
||||
}
|
||||
@@ -233,7 +289,7 @@ watch(
|
||||
|
||||
<template>
|
||||
<section
|
||||
v-if="batch"
|
||||
v-if="batch && !batchDismissed"
|
||||
class="batch-status"
|
||||
:aria-label="$t('documentCollection.importDoc.batchStatus')"
|
||||
>
|
||||
@@ -295,6 +351,16 @@ watch(
|
||||
>
|
||||
{{ $t('documentCollection.importDoc.continueBatch') }}
|
||||
</ElButton>
|
||||
<ElButton
|
||||
class="batch-status__close"
|
||||
:icon="Close"
|
||||
circle
|
||||
text
|
||||
size="small"
|
||||
:aria-label="$t('documentCollection.importDoc.dismissBatchStatus')"
|
||||
:title="$t('documentCollection.importDoc.dismissBatchStatus')"
|
||||
@click="dismissBatchStatus"
|
||||
/>
|
||||
<ElAlert
|
||||
v-if="batch.status === 'INTERRUPTED'"
|
||||
class="batch-status__alert"
|
||||
@@ -321,7 +387,7 @@ watch(
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
v-else-if="loadError"
|
||||
v-else-if="loadError && !dismissedBatchId"
|
||||
class="batch-status batch-status--error"
|
||||
:aria-label="$t('documentCollection.importDoc.batchStatus')"
|
||||
>
|
||||
@@ -407,6 +473,10 @@ watch(
|
||||
min-width: 48px;
|
||||
}
|
||||
|
||||
.batch-status__close {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.batch-status__alert {
|
||||
flex: 1 0 100%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user