Commit 98604b82 authored by Data Governance Dev's avatar Data Governance Dev

refactor(web): 去掉「部分|全量」radio,所有检查都跑全量

用户反馈:partial/full 选项不再需要,所有检查(包括用户自定义规则)
都跑全量扫描。

改动:
- 删除每条 IND leaf 行的 <el-radio-group class="sample-scope">
- 删除自定义规则卡片 header 的 <el-radio-group>
- 删除 sampleScope ref / SAMPLE_PARTIAL_LIMIT / buildSampleLimitsForSubmit()
- 删除 watch(analysisTree.groups) 里初始化 sampleScope 的循环
- startJob() payload 不再发 sample_limits 字段
- 删除 setup return 中的 sampleScope 相关导出
- 删除 .sample-scope / .sample-scope--leaf / .custom-rule-sample CSS
- .tree-row--group .group-description max-width 从 280px 放宽到 560px
  (radio 让位消失后,description 可以更长)

后端零改动:sample_limit 参数链(models → job_manager → orchestrator
→ step7/step9)保留。前端不发 sample_limits → 后端默认 None = 全量扫描。

附带 UI 改进:进度区显示「失败 N 步(...)」行 + 进度条部分失败变
warning + 状态 tag 显示「completed (N 失败)」,避免静默失败误导用户。
(对应 step6 length_check TypeError 的 bug 修复,wrapper 签名
修改在 3dfd038c commit 已包含)
parent 3dfd038c
...@@ -445,18 +445,6 @@ ...@@ -445,18 +445,6 @@
<el-tag v-if="row.llm_mode === 'required'" size="small" type="warning" effect="plain" class="badge">需 LLM</el-tag> <el-tag v-if="row.llm_mode === 'required'" size="small" type="warning" effect="plain" class="badge">需 LLM</el-tag>
<el-tag v-if="row.required" size="small" type="danger" effect="dark" class="badge">必选</el-tag> <el-tag v-if="row.required" size="small" type="danger" effect="dark" class="badge">必选</el-tag>
</el-checkbox> </el-checkbox>
<!-- 2026-08-13:抽样范围 partial/full radio(per-IND)
key 用 step_id(与后端 sample_limits[step_id] 一致)
位置在 match-inputs(字段名输入框)左侧 -->
<el-radio-group
class="sample-scope sample-scope--leaf"
v-model="sampleScope[row.step_id]"
size="small"
@click.stop
>
<el-radio-button label="partial">部分</el-radio-button>
<el-radio-button label="full">全量</el-radio-button>
</el-radio-group>
<!-- 字段匹配输入框:仅在 step 命中 YAML 配置时显示;跨字段/跨表 indicator 不显示 --> <!-- 字段匹配输入框:仅在 step 命中 YAML 配置时显示;跨字段/跨表 indicator 不显示 -->
<span <span
v-if="row.step_id && matchConfig[row.step_id] && !matchConfig[row.step_id].skip" v-if="row.step_id && matchConfig[row.step_id] && !matchConfig[row.step_id].skip"
...@@ -549,17 +537,6 @@ ...@@ -549,17 +537,6 @@
<el-icon><Operation /></el-icon> <el-icon><Operation /></el-icon>
<span class="card-title">自定义规则</span> <span class="card-title">自定义规则</span>
<span class="dict-status">已添加 <strong style="color: #409eff">{{ customRules.length }}</strong> 项</span> <span class="dict-status">已添加 <strong style="color: #409eff">{{ customRules.length }}</strong> 项</span>
<!-- 2026-08-13:自定义规则抽样范围 partial/full radio
自定义规则不在 analysisTree 的 group 里,独立占一个 key "__custom__" -->
<el-radio-group
class="sample-scope"
v-model="sampleScope['__custom__']"
size="small"
@click.stop
>
<el-radio-button label="partial">部分</el-radio-button>
<el-radio-button label="full">全量</el-radio-button>
</el-radio-group>
<div style="margin-left: auto"> <div style="margin-left: auto">
<el-button size="small" @click.stop="clearCustomRules" :disabled="customRules.length === 0">清空</el-button> <el-button size="small" @click.stop="clearCustomRules" :disabled="customRules.length === 0">清空</el-button>
<el-button <el-button
...@@ -657,17 +634,17 @@ ...@@ -657,17 +634,17 @@
早于 / 晚于。 早于 / 晚于。
在条件组里可点「+ 子条件」加叶子、「+ 嵌套组」加括号层(OR/AND 切换连接方式)。 在条件组里可点「+ 子条件」加叶子、「+ 嵌套组」加括号层(OR/AND 切换连接方式)。
</div> </div>
</div> <!-- 2026-08-13:自定义规则卡也加一个「开始分析」按钮
<!-- 2026-08-13:自定义规则卡也加一个「开始分析」按钮 复用 startJob(),与配置卡互为冗余入口(分析配置折叠时不必再点开)
放在 v-show 外面 → 卡片折叠时也能看到,点开就能直接启动 放在 v-show 里面 → 自定义规则卡折叠时按钮一起收起 -->
(分析配置卡折叠时不必再点开它启动) --> <div class="card-footer-action">
<div class="card-footer-action"> <el-button type="success" @click="startJob" :disabled="!connected || form.tables.length === 0" :loading="submitting">
<el-button type="success" @click="startJob" :disabled="!connected || form.tables.length === 0" :loading="submitting"> <el-icon><VideoPlay /></el-icon>开始分析({{ form.tables.length }} 张表 / {{ form.steps.length }} 项检查)
<el-icon><VideoPlay /></el-icon>开始分析({{ form.tables.length }} 张表 / {{ form.steps.length }} 项检查) </el-button>
</el-button> <el-button v-if="currentJob && jobStatus.status === 'running'" @click="cancelJob" type="danger" plain>
<el-button v-if="currentJob && jobStatus.status === 'running'" @click="cancelJob" type="danger" plain> <el-icon><Close /></el-icon>取消任务
<el-icon><Close /></el-icon>取消任务 </el-button>
</el-button> </div>
</div> </div>
</el-card> </el-card>
...@@ -839,15 +816,24 @@ ...@@ -839,15 +816,24 @@
<div class="card-header"> <div class="card-header">
<el-icon><Loading /></el-icon> <el-icon><Loading /></el-icon>
<span class="card-title">分析进度</span> <span class="card-title">分析进度</span>
<el-tag :type="statusTagType">{{ jobStatus.status }}</el-tag> <el-tag :type="statusTagType">{{ statusLabel }}</el-tag>
</div> </div>
</template> </template>
<div class="progress-block"> <div class="progress-block">
<el-progress :percentage="jobStatus.progress_pct" :status="jobStatus.status === 'failed' ? 'exception' : (jobStatus.status === 'completed' ? 'success' : '')" /> <el-progress :percentage="jobStatus.progress_pct" :status="progressBarStatus" />
<div class="progress-info"> <div class="progress-info">
<div>当前: {{ jobStatus.current_step_title || '—' }}</div> <div>当前: {{ jobStatus.current_step_title || '—' }}</div>
<div>已完成: {{ jobStatus.steps_completed.length }} / {{ jobStatus.steps_planned.length }}</div> <div>已完成: {{ jobStatus.steps_completed.length }} / {{ jobStatus.steps_planned.length }}</div>
<!-- 2026-08-13:失败步骤计数。
status='completed' 但 steps_failed 非空时,job 整体没抛
RuntimeError 也能完成(因为失败 step 都是非关键的),此时
UI 应明确提示「部分失败」,避免误以为全部成功。 -->
<div v-if="jobStatus.steps_failed && jobStatus.steps_failed.length > 0"
class="error-info"
:title="jobStatus.steps_failed.join(', ')">
失败: {{ jobStatus.steps_failed.length }} 步({{ jobStatus.steps_failed.join(', ') }})
</div>
<div v-if="jobStatus.error" class="error-info">错误: {{ jobStatus.error }}</div> <div v-if="jobStatus.error" class="error-info">错误: {{ jobStatus.error }}</div>
</div> </div>
</div> </div>
...@@ -1219,22 +1205,8 @@ ...@@ -1219,22 +1205,8 @@
// analysisTree 加载后不再自动展开任何组;保留该 watcher 占位 // analysisTree 加载后不再自动展开任何组;保留该 watcher 占位
// 以备未来需要在此做初始化(例如按 group key 推断默认勾选)。 // 以备未来需要在此做初始化(例如按 group key 推断默认勾选)。
// 2026-08-13:顺便初始化 sampleScope 默认值 'partial',避免 v-model 绑定 undefined。
// per-IND radio 改成 key=step_id(每项独立配置),不再按 group 共享。
watch(() => analysisTree.value.groups, (_groups) => { watch(() => analysisTree.value.groups, (_groups) => {
/* 默认全部折叠 —— 不做任何自动展开 */ /* 默认全部折叠 —— 不做任何自动展开 */
if (Array.isArray(_groups)) {
for (const g of _groups) {
for (const c of (g.checks || [])) {
if (c && c.step_id && !sampleScope.value[c.step_id]) {
sampleScope.value[c.step_id] = 'partial';
}
}
}
}
if (!sampleScope.value['__custom__']) {
sampleScope.value['__custom__'] = 'partial';
}
}, { immediate: true }); }, { immediate: true });
// ── 数据字典浏览器(2026-08-13 改为后端分页动态加载)── // ── 数据字典浏览器(2026-08-13 改为后端分页动态加载)──
...@@ -1502,19 +1474,49 @@ ...@@ -1502,19 +1474,49 @@
// - value = 'partial'(默认,前 N 行抽样,partial_limit=1000) // - value = 'partial'(默认,前 N 行抽样,partial_limit=1000)
// | 'full'(全量,不加 LIMIT) // | 'full'(全量,不加 LIMIT)
// analysisTree 加载后 watcher 会为每个 step_id 初始化 'partial', // analysisTree 加载后 watcher 会为每个 step_id 初始化 'partial',
// 避免 v-model 绑定 undefined 导致 radio 不显示选中态。 // 2026-08-13:去掉「部分 / 全量」radio 后,下方常量已无引用。
const sampleScope = ref({}); // (保持注释说明历史决策原因;如未来再引入抽样,可参考旧值 SAMPLE_PARTIAL_LIMIT=1000)
const SAMPLE_PARTIAL_LIMIT = 1000; // 前端默认 partial 上限(与后端约定)
const logs = ref([]); const logs = ref([]);
const logExpanded = ref(false); // 2026-08-12 改:默认折叠实时日志 const logExpanded = ref(false); // 2026-08-12 改:默认折叠实时日志
const showStandardsDialog = ref(false); const showStandardsDialog = ref(false);
let logEventSource = null; let logEventSource = null;
let pollTimer = null; let pollTimer = null;
const statusTagType = computed(() => ({ const statusTagType = computed(() => {
pending: 'info', running: 'warning', completed: 'success', const s = jobStatus.value.status;
failed: 'danger', cancelled: 'info', // 2026-08-13:status='completed' 但有失败步骤 → 视作「部分失败」,
}[jobStatus.value.status] || 'info')); // 标签用 warning(橙色),避免误以为全部成功。
if (s === 'completed'
&& jobStatus.value.steps_failed
&& jobStatus.value.steps_failed.length > 0) {
return 'warning';
}
return ({
pending: 'info', running: 'warning', completed: 'success',
failed: 'danger', cancelled: 'info',
}[s] || 'info');
});
// 进度条状态:failed → exception;部分失败 → warning;completed → success;其它空
const progressBarStatus = computed(() => {
const s = jobStatus.value.status;
if (s === 'failed') return 'exception';
if (s === 'completed'
&& jobStatus.value.steps_failed
&& jobStatus.value.steps_failed.length > 0) {
return 'warning';
}
return s === 'completed' ? 'success' : '';
});
// 状态 tag 显示文本:completed + 失败 → "completed (N 失败)"
const statusLabel = computed(() => {
const s = jobStatus.value.status;
if (s === 'completed'
&& jobStatus.value.steps_failed
&& jobStatus.value.steps_failed.length > 0) {
return `completed (${jobStatus.value.steps_failed.length} 失败)`;
}
return s;
});
// ── 检查项树形勾选(form.steps 是唯一状态源,view 派生) ── // ── 检查项树形勾选(form.steps 是唯一状态源,view 派生) ──
// flatCheckRows: UI 实际渲染的平铺行(expanded 控制 leaf 可见性) // flatCheckRows: UI 实际渲染的平铺行(expanded 控制 leaf 可见性)
...@@ -2165,9 +2167,8 @@ ...@@ -2165,9 +2167,8 @@
match_overrides: buildMatchOverridesForSubmit(), match_overrides: buildMatchOverridesForSubmit(),
// 自定义规则:保留 user_input 非空的项;空关键字在后端会被静默跳过 // 自定义规则:保留 user_input 非空的项;空关键字在后端会被静默跳过
custom_rules: buildCustomRulesForSubmit(), custom_rules: buildCustomRulesForSubmit(),
// 抽样范围:partial(默认 1000)/ full(无 LIMIT) // 2026-08-13:去掉「部分 / 全量」radio 后不再发送 sample_limits;
// 后端 models.sample_limits: dict[step_id, int|None] // 后端 sample_limit 默认 None 即全量扫描。
sample_limits: buildSampleLimitsForSubmit(),
}; };
try { try {
const r = await fetch('/api/jobs', { const r = await fetch('/api/jobs', {
...@@ -2258,28 +2259,9 @@ ...@@ -2258,28 +2259,9 @@
} }
// ── 提交时把 sampleScope 转成后端要的 {step_id: int|None} 字典 ── // ── 提交时把 sampleScope 转成后端要的 {step_id: int|None} 字典 ──
// 输入:sampleScope.value = { '<step_id>': 'partial'|'full' (per-IND radio) // 2026-08-13:去掉「部分 / 全量」radio 后,本函数删除;
// '__custom__': 'partial'|'full' (自定义规则 card 共享) } // 前端不再发送 sample_limits 字段;后端 sample_limit 默认 None 即全量扫描。
// 输出:{ '<step_id>': <int>|None, ... } // (历史:SAMPLE_PARTIAL_LIMIT=1000;partial/full 通过 sampleScope[step_id] 控制)
// - 'full' → None (后端会移除 LIMIT N / AND ROWNUM<=N)
// - 'partial' → SAMPLE_PARTIAL_LIMIT (1000)
// - 缺省值 → 'partial'(默认部分;与 UI 默认一致)
// 自定义规则固定映射到 step_id='custom_value_check'(后端 step9 标识;
// 同一个 step_id 共享一个 sample_limit —— step9 内部对所有规则用同一 LIMIT)
function buildSampleLimitsForSubmit() {
const out = {};
const groups = (analysisTree.value && analysisTree.value.groups) || [];
for (const g of groups) {
for (const c of (g.checks || [])) {
const scope = sampleScope.value[c.step_id];
out[c.step_id] = scope === 'full' ? null : SAMPLE_PARTIAL_LIMIT;
}
}
// 自定义规则(独立卡片,不在 analysisTree 的 group 里;card 内多规则共享一个 limit)
const customScope = sampleScope.value['__custom__'];
out['custom_value_check'] = customScope === 'full' ? null : SAMPLE_PARTIAL_LIMIT;
return out;
}
// ── 订阅日志 ── // ── 订阅日志 ──
function subscribeLogs(jobId) { function subscribeLogs(jobId) {
...@@ -2577,7 +2559,7 @@ ...@@ -2577,7 +2559,7 @@
onTablesScroll, onFieldsScroll, onTablesScroll, onFieldsScroll,
onTableSelectionChange, selectAllTables, selectNoTables, selectTablesInvert, onTableSelectionChange, selectAllTables, selectNoTables, selectTablesInvert,
disconnectDataDict, disconnectDataDict,
currentJob, jobStatus, statusTagType, currentJob, jobStatus, statusTagType, statusLabel, progressBarStatus,
logs, logExpanded, showStandardsDialog, logs, logExpanded, showStandardsDialog,
jobResult, activeTab, jobResult, activeTab,
displayedTabs, displayedKpis, displayedTabs, displayedKpis,
...@@ -2593,7 +2575,6 @@ ...@@ -2593,7 +2575,6 @@
analysisConfigCollapsed, dataDictCollapsed, connectionCollapsed, analysisConfigCollapsed, dataDictCollapsed, connectionCollapsed,
customRules, customConfigCollapsed, customRules, customConfigCollapsed,
addCustomRule, removeCustomRule, clearCustomRules, addCustomRule, removeCustomRule, clearCustomRules,
sampleScope, SAMPLE_PARTIAL_LIMIT, buildSampleLimitsForSubmit,
fieldClassTagType, fieldClassTagType,
onDbTypeChange, onDbTypeChange,
selectedField, selectFieldFilter, clearFieldFilter, selectedField, selectFieldFilter, clearFieldFilter,
......
...@@ -296,34 +296,13 @@ body { ...@@ -296,34 +296,13 @@ body {
} }
/* 2026-08-13:抽样范围 partial/full radio /* 2026-08-13:抽样范围 partial/full radio
- 限制 description 最大宽度,给 radio 让位(避免换行错位) - 限制 description 最大宽度,给 radio 让位(避免换行错位)
- radio 自身在行内最右,flex-shrink:0 防被挤压 */ - radio 自身在行内最右,flex-shrink:0 防被挤压
- 2026-08-13:去掉 radio 后 group-description 不再需要给 radio 让位,
max-width 调整为更宽松的值,description 可显示更长文案 */
.tree-row--group .group-description { .tree-row--group .group-description {
max-width: 280px; max-width: 560px;
flex: 0 1 auto; flex: 0 1 auto;
} }
.sample-scope {
margin-left: 12px;
flex-shrink: 0;
}
.sample-scope .el-radio-button__inner {
padding: 5px 10px;
font-size: 12px;
}
/* 叶子行(per-IND)的 radio:与 match-inputs 之间紧贴,左边距小一些 */
.sample-scope--leaf {
margin-left: 8px;
}
/* 自定义规则卡片里也加一份 radio(在 card 顶部一行右对齐) */
.custom-rule-sample {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
}
.custom-rule-sample .label {
color: #606266;
font-size: 13px;
}
.tree-row .badge { .tree-row .badge {
margin-left: 6px; margin-left: 6px;
height: 18px; height: 18px;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment