|
|
@@ -11,13 +11,7 @@
|
|
|
</span>
|
|
|
<div class="page-title-wrap">
|
|
|
<div class="page-title">{{ isComplete ? '数据清洗结果' : '数据清洗进行中' }}</div>
|
|
|
- <div class="page-subtitle">
|
|
|
- {{
|
|
|
- isComplete
|
|
|
- ? '查看本次清洗、入库与治理的执行结果,以及完整任务日志。'
|
|
|
- : '实时展示文件清洗、入库与治理的执行进度,便于快速确认任务运行状态。'
|
|
|
- }}
|
|
|
- </div>
|
|
|
+ <div class="page-subtitle">{{ pageSubtitle }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div v-if="!isComplete" class="sse-pill" :class="{ 'is-on': sseConnected }">
|
|
|
@@ -105,7 +99,7 @@
|
|
|
</div>
|
|
|
</aside>
|
|
|
|
|
|
- <section class="run-log" :class="{ 'is-govern': activeStep >= 2 }">
|
|
|
+ <section class="run-log" :class="{ 'is-govern': shouldRunGovernAfterClean && activeStep >= 2 }">
|
|
|
<TaskLogList
|
|
|
:logs="taskLog"
|
|
|
title="处理日志"
|
|
|
@@ -169,10 +163,7 @@
|
|
|
</div>
|
|
|
<div class="complete-actions">
|
|
|
<Button class="complete-log" @click="handleViewLog">查看清洗日志</Button>
|
|
|
- <Button v-if="isSmartCleanEntry" type="primary" class="complete-result" @click="handleStartAnalysis">
|
|
|
- 开始分析
|
|
|
- </Button>
|
|
|
- <Button v-else type="primary" class="complete-result" @click="handleViewResult">查看结果</Button>
|
|
|
+ <Button type="primary" class="complete-result" @click="handleViewResult">查看结果</Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -218,7 +209,6 @@
|
|
|
clearImportProgress,
|
|
|
clearLegacyTaskLogStorage,
|
|
|
getActiveImportProgress,
|
|
|
- getCleanFlowSource,
|
|
|
getImportProgressEventSource,
|
|
|
resetImportProgressRuntimeState,
|
|
|
saveImportProgress,
|
|
|
@@ -270,12 +260,20 @@
|
|
|
const governFailed = ref(false);
|
|
|
let taskLogSeq = 0;
|
|
|
const logModalOpen = ref(false);
|
|
|
- /** 执行中左侧的步骤说明:activeStep 0/1/2 与之一一对应 */
|
|
|
- const runSteps = [
|
|
|
- { title: '读取数据', desc: '解析上传文件与工作表' },
|
|
|
- { title: '清洗入库', desc: '按模板清洗并写入案件库' },
|
|
|
- { title: '数据治理', desc: '去重、关系识别与分类树统计' },
|
|
|
- ];
|
|
|
+ /**
|
|
|
+ * 执行中左侧的步骤说明:activeStep 0/1(开启治理时含 2)与之一一对应。
|
|
|
+ * 「数据治理」只在治理设置开启自动治理时展示;未开启时流程只有「读取数据 → 清洗入库」两步。
|
|
|
+ */
|
|
|
+ const runSteps = computed(() => {
|
|
|
+ const steps = [
|
|
|
+ { title: '读取数据', desc: '解析上传文件与工作表' },
|
|
|
+ { title: '清洗入库', desc: '按模板清洗并写入案件库' },
|
|
|
+ ];
|
|
|
+ if (shouldRunGovernAfterClean.value) {
|
|
|
+ steps.push({ title: '数据治理', desc: '去重、关系识别与分类树统计' });
|
|
|
+ }
|
|
|
+ return steps;
|
|
|
+ });
|
|
|
let eventSource = getImportProgressEventSource();
|
|
|
|
|
|
/**
|
|
|
@@ -311,8 +309,8 @@
|
|
|
const RING_CIRCUMFERENCE = 2 * Math.PI * 50;
|
|
|
const progressDash = computed(() => (overallProgress.value / 100) * RING_CIRCUMFERENCE);
|
|
|
const currentStepLabel = computed(() => {
|
|
|
- if (activeStep.value >= 2) return '数据治理';
|
|
|
- if (activeStep.value === 1) return '清洗入库';
|
|
|
+ if (shouldRunGovernAfterClean.value && activeStep.value >= 2) return '数据治理';
|
|
|
+ if (activeStep.value >= 1) return '清洗入库';
|
|
|
return '读取数据';
|
|
|
});
|
|
|
const progressStatusText = computed(() => {
|
|
|
@@ -506,10 +504,7 @@
|
|
|
logModalOpen.value = true;
|
|
|
};
|
|
|
|
|
|
- /**
|
|
|
- * 自主页面发起的清洗,完成出口「查看结果」:跳清洗结果统计页(导入文件列表带数据量/成功失败条数)。
|
|
|
- * 智能页面发起的清洗对应出口是「开始分析」(见 {@link handleStartAnalysis})。
|
|
|
- */
|
|
|
+ /** 完成出口「查看结果」:跳清洗结果统计页(导入文件列表带数据量/成功失败条数) */
|
|
|
const handleViewResult = async () => {
|
|
|
if (!shouldRunGovernAfterClean.value) {
|
|
|
createMessage.warning('数据治理已关闭,本次导入未自动治理,可在数据页手动执行');
|
|
|
@@ -517,26 +512,15 @@
|
|
|
await navigateToImportResult();
|
|
|
};
|
|
|
|
|
|
- /** 智能页面发起的清洗,完成出口「开始分析」:直接进案件研判(智能工作台)新对话草稿 */
|
|
|
- const handleStartAnalysis = async () => {
|
|
|
- if (!shouldRunGovernAfterClean.value) {
|
|
|
- createMessage.warning('数据治理已关闭,本次导入未自动治理,可在数据页手动执行');
|
|
|
- }
|
|
|
- await go({ path: PageEnum.BASE_WORKBENCH, query: { newChat: '1' } }, true);
|
|
|
- };
|
|
|
-
|
|
|
const navigateToImportResult = async () => {
|
|
|
sessionStorage.removeItem(IMPORT_FILE_INFO_STORAGE_KEY);
|
|
|
sessionStorage.removeItem(RECLEAN_CONTEXT_STORAGE_KEY);
|
|
|
markAnalysisDataChanged();
|
|
|
// 落到「导入文件」页签:列表带每个文件的数据量与成功/失败条数,即清洗结果统计。
|
|
|
- // 页签与清洗来源(自主/智能)经 sessionStorage 一次性传递——目标页被 keep-alive 缓存,
|
|
|
+ // 页签与完成出口标记经 sessionStorage 一次性传递——目标页被 keep-alive 缓存,
|
|
|
// 激活时机由 onActivated 消费该上下文决定。
|
|
|
// 跳转**不带任何 URL 参数**:「案件数据」是固定页签,带 query 会让 URL 变脏、也容易把页签身份搞乱。
|
|
|
- sessionStorage.setItem(
|
|
|
- CLEAN_RESULT_CONTEXT_KEY,
|
|
|
- JSON.stringify({ tab: 'files', mode: isSmartCleanEntry.value ? 'smart' : 'manual' }),
|
|
|
- );
|
|
|
+ sessionStorage.setItem(CLEAN_RESULT_CONTEXT_KEY, JSON.stringify({ tab: 'files', mode: 'manual' }));
|
|
|
await go({ path: PageEnum.BASE_HOME }, true);
|
|
|
await nextTick();
|
|
|
emitter.emit(CASE_DATA_REFRESH_EVENT);
|
|
|
@@ -660,18 +644,8 @@
|
|
|
const getRequestErrorMessage = (error: any, fallback = '操作失败') =>
|
|
|
error?.response?.data?.message || error?.message || fallback;
|
|
|
|
|
|
- /**
|
|
|
- * 是否「智能入口」发起:智能界面进入的上传/清洗流程默认自动治理,忽略治理配置;
|
|
|
- * 自主入口(含直接打开本页)继续遵守治理配置。
|
|
|
- */
|
|
|
- const isSmartFlowEntry = () => getCleanFlowSource() === 'smart';
|
|
|
-
|
|
|
+ /** 是否在清洗完成后自动治理:按「同时治理」配置执行 */
|
|
|
const loadGovernSwitch = async () => {
|
|
|
- // 智能入口:默认自动治理,直接忽略治理配置(也不发起配置查询)
|
|
|
- if (isSmartFlowEntry()) {
|
|
|
- shouldRunGovernAfterClean.value = true;
|
|
|
- return;
|
|
|
- }
|
|
|
try {
|
|
|
const list = await getGovernConfList();
|
|
|
const hasGovernConf = (Array.isArray(list) ? list : []).find(
|
|
|
@@ -1031,16 +1005,14 @@
|
|
|
persistImportProgress();
|
|
|
try {
|
|
|
const reCleanContext = loadReCleanContext();
|
|
|
- const autoGovern = isSmartFlowEntry();
|
|
|
await doClean(
|
|
|
reCleanContext
|
|
|
? {
|
|
|
fileInfos,
|
|
|
reClean: 1,
|
|
|
fileId: reCleanContext.fileId,
|
|
|
- autoGovern,
|
|
|
}
|
|
|
- : { fileInfos, autoGovern },
|
|
|
+ : { fileInfos },
|
|
|
);
|
|
|
// 清洗任务已发起:文件列表已读入 currentCleanFiles、进度快照由 persistImportProgress 维护,
|
|
|
// 因此立即清空「已上传文件缓存」与清洗流程中间缓存,避免后续进入流程时复用上一批文件。
|
|
|
@@ -1072,19 +1044,6 @@
|
|
|
const aiStage = ref<SmartCleanStatusVO | null>(null);
|
|
|
const aiStageTimeout = ref(false);
|
|
|
|
|
|
- /**
|
|
|
- * 完成面板第二个按钮按「清洗从哪个页面发起」区分:
|
|
|
- * - 自主页面(传统壳:案件数据 → 上传数据 → 清洗)→「查看结果」(清洗结果统计页)
|
|
|
- * - 智能页面(智能工作台:研判方案视图 → 上传文件 → 清洗)→「开始分析」(回工作台新对话)
|
|
|
- *
|
|
|
- * 来源由上传页启动清洗流程时写入 sessionStorage(见 importProgressSession.setCleanFlowSource);
|
|
|
- * 拿不到时退回旧的 fused 判断兜底(仅智能清洗链路会带 fused=1)。
|
|
|
- */
|
|
|
- const isSmartCleanEntry = computed(() => {
|
|
|
- const source = getCleanFlowSource();
|
|
|
- return source ? source === 'smart' : isFused.value;
|
|
|
- });
|
|
|
-
|
|
|
/** 交给 AI 判定的表数 = 全部 sheet - 原本就匹配上模板的 sheet */
|
|
|
const aiSheetTotal = computed(() =>
|
|
|
Math.max(0, (aiStage.value?.sheetTotal ?? 0) - (aiStage.value?.matchedCount ?? 0)),
|
|
|
@@ -1133,10 +1092,17 @@
|
|
|
*/
|
|
|
const isComplete = computed(() => governFailed.value || completionFallback.value || baseComplete.value);
|
|
|
|
|
|
+ /** 页面说明文案:治理未开启时不出现「治理」,与左侧步骤条保持一致 */
|
|
|
+ const pageSubtitle = computed(() => {
|
|
|
+ const scope = shouldRunGovernAfterClean.value ? '清洗、入库与治理' : '清洗与入库';
|
|
|
+ return isComplete.value
|
|
|
+ ? `查看本次${scope}的执行结果,以及完整任务日志。`
|
|
|
+ : `实时展示文件${scope}的执行进度,便于快速确认任务运行状态。`;
|
|
|
+ });
|
|
|
+
|
|
|
/**
|
|
|
* 完成时提示「另有 N 个表 AI 无法清洗」——只在完成那一刻提醒一次。
|
|
|
- * 完成后<b>不自动跳转</b>(2026-10-11 需求):跳转由完成面板上的按钮触发,
|
|
|
- * 自主页面发起 →「查看结果」,智能页面发起 →「开始分析」。
|
|
|
+ * 完成后<b>不自动跳转</b>(2026-10-11 需求):跳转由完成面板上的「查看结果」按钮触发。
|
|
|
*/
|
|
|
watch(isComplete, (done) => {
|
|
|
if (!done) return;
|
|
|
@@ -1332,11 +1298,6 @@
|
|
|
clearLegacyTaskLogStorage();
|
|
|
|
|
|
if (restoreImportProgress()) {
|
|
|
- // 刷新恢复:智能入口仍强制自动治理,避免旧快照把治理开关带回配置值
|
|
|
- if (isSmartFlowEntry()) {
|
|
|
- shouldRunGovernAfterClean.value = true;
|
|
|
- persistImportProgress();
|
|
|
- }
|
|
|
eventSource = getImportProgressEventSource();
|
|
|
if (!eventSource) {
|
|
|
connectSse();
|