fix: 移除 FaceFilterSettings 重复字段,与 Rust 端结构一致

问题修正:
- 移除 FaceFilterSettings 中的重复字段
- 简化为三段式结构(ui_filter_rules, processing_thresholds, trace_settings)
- 新增 version 字段用于版本控制
- 删除 useFaceFilter.ts 中的 rules 变量
- 重命名 faceFilterSettings 为 settings
- 更新所有访问方式为 settings.value.ui_filter_rules.*

变更内容:
1. src/types/faceFilter.ts
   - FaceFilterSettings 只保留三段式结构
   - fromLegacy() 和 defaults() 不再返回重复字段

2. src/composables/useFaceFilter.ts
   - 删除 rules 变量(不再需要)
   - 删除 defaultRules 常量
   - 重命名 faceFilterSettings 为 settings
   - 更新 localStorage 逻辑保存完整 settings
   - 所有访问改为 settings.value.ui_filter_rules.*

3. src/components/FaceFilterPanel.vue
   - 更新 v-model 绑定为 settings.trace_settings.*
   - 更新解构,删除 rules
   - 更新所有访问为 settings.ui_filter_rules.*

测试:
- ✅ TypeScript 编译通过
- ✅ Vite build 成功

对应 Core API: 86c776ce
修正文档: docs/FACE_FILTER_TYPES_FIX_REQUIRED.md
This commit is contained in:
2026-10-09 15:55:30 +08:00
parent 6423eeed77
commit 8789770af2
3 changed files with 53 additions and 92 deletions

View File

@@ -57,7 +57,7 @@
<label>Confidence:</label> <label>Confidence:</label>
<input <input
type="number" type="number"
v-model.number="rules.min_confidence" v-model.number="settings.ui_filter_rules.min_confidence"
@change="onRuleChange" @change="onRuleChange"
min="0" min="0"
max="1" max="1"
@@ -72,7 +72,7 @@
<label>Bbox Size:</label> <label>Bbox Size:</label>
<input <input
type="number" type="number"
v-model.number="rules.min_bbox_size" v-model.number="settings.ui_filter_rules.min_bbox_size"
@change="onRuleChange" @change="onRuleChange"
min="0" min="0"
step="10" step="10"
@@ -84,7 +84,7 @@
<div class="filter-row"> <div class="filter-row">
<label>Source:</label> <label>Source:</label>
<select v-model="rules.source" @change="onRuleChange" class="filter-select"> <select v-model="settings.ui_filter_rules.source" @change="onRuleChange" class="filter-select">
<option value="all">All</option> <option value="all">All</option>
<option value="apple_vision">Apple Vision</option> <option value="apple_vision">Apple Vision</option>
<option value="mediapipe">MediaPipe</option> <option value="mediapipe">MediaPipe</option>
@@ -93,7 +93,7 @@
<div class="filter-row"> <div class="filter-row">
<label>Backend Status:</label> <label>Backend Status:</label>
<select v-model="rules.backend_status" @change="onRuleChange" class="filter-select"> <select v-model="settings.ui_filter_rules.backend_status" @change="onRuleChange" class="filter-select">
<option value="all">All</option> <option value="all">All</option>
<option value="accept">Accept</option> <option value="accept">Accept</option>
<option value="review">Review</option> <option value="review">Review</option>
@@ -108,7 +108,7 @@
<label class="checkbox-label"> <label class="checkbox-label">
<input <input
type="checkbox" type="checkbox"
v-model="faceFilterSettings.single_frame_trace_vlm_enabled" v-model="settings.trace_settings.single_frame_trace_vlm_enabled"
@change="markModified" @change="markModified"
/> />
VLM Processing VLM Processing
@@ -120,7 +120,7 @@
<label class="checkbox-label"> <label class="checkbox-label">
<input <input
type="checkbox" type="checkbox"
v-model="faceFilterSettings.single_frame_trace_group_enabled" v-model="settings.trace_settings.single_frame_trace_group_enabled"
@change="markModified" @change="markModified"
/> />
Face Groups Face Groups
@@ -132,7 +132,7 @@
<label class="checkbox-label"> <label class="checkbox-label">
<input <input
type="checkbox" type="checkbox"
v-model="faceFilterSettings.single_frame_trace_unassigned_enabled" v-model="settings.trace_settings.single_frame_trace_unassigned_enabled"
@change="markModified" @change="markModified"
/> />
Unassigned List Unassigned List
@@ -144,7 +144,7 @@
<label class="checkbox-label"> <label class="checkbox-label">
<input <input
type="checkbox" type="checkbox"
v-model="faceFilterSettings.single_trace_group_filter_enabled" v-model="settings.trace_settings.single_trace_group_filter_enabled"
@change="markModified" @change="markModified"
/> />
Single Trace Groups Single Trace Groups
@@ -154,19 +154,19 @@
<div class="section-actions"> <div class="section-actions">
<div class="auto-save-hint"> <div class="auto-save-hint">
{{ faceFilterSettingsLoading ? '⏳ Saving...' : '✓ Auto-saved' }} {{ settingsLoading ? '⏳ Saving...' : '✓ Auto-saved' }}
</div> </div>
<button <button
class="action-btn" class="action-btn"
@click="handleResetSingleFrame" @click="handleResetSingleFrame"
:disabled="faceFilterSettingsLoading" :disabled="settingsLoading"
> >
Reset to Default Reset to Default
</button> </button>
</div> </div>
<div v-if="faceFilterSettingsError" class="error-message"> <div v-if="settingsError" class="error-message">
❌ {{ faceFilterSettingsError }} ❌ {{ settingsError }}
</div> </div>
</div> </div>
@@ -232,7 +232,6 @@ const emit = defineEmits<{
const { const {
mode, mode,
rules,
filterResult, filterResult,
filterStats, filterStats,
loading, loading,
@@ -242,14 +241,14 @@ const {
updateRules, updateRules,
applyBackendFilter, applyBackendFilter,
resetToDefault, resetToDefault,
faceFilterSettings, settings,
faceFilterSettingsLoading, settingsLoading,
faceFilterSettingsError, settingsError,
faceFilterSettingsModified, settingsModified,
loadSingleFrameSettings, loadSingleFrameSettings,
resetSingleFrameSettings, resetSingleFrameSettings,
markSingleFrameSettingsModified, markSingleFrameSettingsModified,
cleanupAutoSave cleanupAutoSave,
} = useFaceFilter(); } = useFaceFilter();
const showPanel = ref(false); const showPanel = ref(false);
@@ -259,7 +258,7 @@ function togglePanel() {
} }
function onRuleChange() { function onRuleChange() {
updateRules(rules.value); updateRules(settings.value.ui_filter_rules);
} }
async function handleApplyBackend() { async function handleApplyBackend() {
@@ -291,7 +290,7 @@ watch(showPanel, (show) => {
} }
}); });
watch(faceFilterSettingsModified, (newVal, oldVal) => { watch(settingsModified, (newVal, oldVal) => {
if (oldVal === true && newVal === false) { if (oldVal === true && newVal === false) {
emit('settings-updated'); emit('settings-updated');
} }

View File

@@ -97,13 +97,6 @@ export interface FileProfileResponse {
face_filter: FaceFilterSettings; face_filter: FaceFilterSettings;
} }
const defaultRules: UiFilterRules = {
min_confidence: 0.5,
min_bbox_size: 80,
source: 'all',
backend_status: 'accept'
};
const STORAGE_KEY = 'momentry_face_filter_preferences'; const STORAGE_KEY = 'momentry_face_filter_preferences';
// API helpers // API helpers
@@ -196,7 +189,7 @@ function hybridFilter(faces: FaceMetadata[], rules: UiFilterRules): FaceMetadata
export function useFaceFilter() { export function useFaceFilter() {
const mode = ref<'instant' | 'backend' | 'hybrid'>('instant'); const mode = ref<'instant' | 'backend' | 'hybrid'>('instant');
const rules = ref<UiFilterRules>({ ...defaultRules }); const settings = ref<FaceFilterSettings>(FaceFilterSettingsMigration.defaults());
const allFaces = ref<FaceMetadata[]>([]); const allFaces = ref<FaceMetadata[]>([]);
const filteredFaces = ref<FaceMetadata[]>([]); const filteredFaces = ref<FaceMetadata[]>([]);
const filterResult = ref<FilterResult | null>(null); const filterResult = ref<FilterResult | null>(null);
@@ -204,10 +197,9 @@ export function useFaceFilter() {
const loading = ref(false); const loading = ref(false);
const error = ref<string | null>(null); const error = ref<string | null>(null);
const faceFilterSettings = ref<FaceFilterSettings>(FaceFilterSettingsMigration.defaults()); const settingsLoading = ref(false);
const faceFilterSettingsLoading = ref(false); const settingsError = ref<string | null>(null);
const faceFilterSettingsError = ref<string | null>(null); const settingsModified = ref(false);
const faceFilterSettingsModified = ref(false);
const currentFileUuid = ref<string | null>(null); const currentFileUuid = ref<string | null>(null);
let autoSaveTimer: ReturnType<typeof setTimeout> | null = null; let autoSaveTimer: ReturnType<typeof setTimeout> | null = null;
@@ -219,26 +211,24 @@ export function useFaceFilter() {
removed: allFaces.value.length - filteredFaces.value.length removed: allFaces.value.length - filteredFaces.value.length
})); }));
// Load preferences from localStorage
function loadPreferences() { function loadPreferences() {
try { try {
const stored = localStorage.getItem(STORAGE_KEY); const stored = localStorage.getItem(STORAGE_KEY);
if (stored) { if (stored) {
const prefs = JSON.parse(stored); const prefs = JSON.parse(stored);
mode.value = prefs.mode || 'instant'; mode.value = prefs.mode || 'instant';
rules.value = prefs.rules || { ...defaultRules }; settings.value = prefs.settings || FaceFilterSettingsMigration.defaults();
} }
} catch (err) { } catch (err) {
console.error('Failed to load preferences:', err); console.error('Failed to load preferences:', err);
} }
} }
// Save preferences to localStorage
function savePreferences() { function savePreferences() {
try { try {
localStorage.setItem(STORAGE_KEY, JSON.stringify({ localStorage.setItem(STORAGE_KEY, JSON.stringify({
mode: mode.value, mode: mode.value,
rules: rules.value settings: settings.value
})); }));
} catch (err) { } catch (err) {
console.error('Failed to save preferences:', err); console.error('Failed to save preferences:', err);
@@ -297,11 +287,10 @@ export function useFaceFilter() {
}); });
} }
// Apply local filter based on mode
function applyLocalFilter() { function applyLocalFilter() {
switch (mode.value) { switch (mode.value) {
case 'instant': case 'instant':
filteredFaces.value = quickFilter(allFaces.value, rules.value); filteredFaces.value = quickFilter(allFaces.value, settings.value.ui_filter_rules);
break; break;
case 'backend': case 'backend':
filteredFaces.value = allFaces.value.filter(face => filteredFaces.value = allFaces.value.filter(face =>
@@ -309,13 +298,13 @@ export function useFaceFilter() {
); );
break; break;
case 'hybrid': case 'hybrid':
filteredFaces.value = hybridFilter(allFaces.value, rules.value); filteredFaces.value = hybridFilter(allFaces.value, settings.value.ui_filter_rules);
break; break;
} }
} }
function updateRules(newRules: Partial<UiFilterRules>) { function updateRules(newRules: Partial<UiFilterRules>) {
rules.value = { ...rules.value, ...newRules }; settings.value.ui_filter_rules = { ...settings.value.ui_filter_rules, ...newRules };
applyLocalFilter(); applyLocalFilter();
savePreferences(); savePreferences();
} }
@@ -371,52 +360,51 @@ function updateRules(newRules: Partial<UiFilterRules>) {
} }
} }
// Reset to default
function resetToDefault() { function resetToDefault() {
rules.value = { ...defaultRules }; settings.value = FaceFilterSettingsMigration.defaults();
applyLocalFilter(); applyLocalFilter();
savePreferences(); savePreferences();
} }
async function loadSingleFrameSettings(fileUuid: string) { async function loadSingleFrameSettings(fileUuid: string) {
faceFilterSettingsLoading.value = true; settingsLoading.value = true;
faceFilterSettingsError.value = null; settingsError.value = null;
try { try {
currentFileUuid.value = fileUuid; currentFileUuid.value = fileUuid;
const profile = await getFileProfile(fileUuid); const profile = await getFileProfile(fileUuid);
if (profile.face_filter) { if (profile.face_filter) {
faceFilterSettings.value = profile.face_filter; settings.value = profile.face_filter;
} else { } else {
faceFilterSettings.value = FaceFilterSettingsMigration.defaults(); settings.value = FaceFilterSettingsMigration.defaults();
} }
faceFilterSettingsModified.value = false; settingsModified.value = false;
} catch (err: any) { } catch (err: any) {
faceFilterSettingsError.value = err.message || 'Failed to load settings'; settingsError.value = err.message || 'Failed to load settings';
console.error('Failed to load single frame settings:', err); console.error('Failed to load single frame settings:', err);
} finally { } finally {
faceFilterSettingsLoading.value = false; settingsLoading.value = false;
} }
} }
async function saveSingleFrameSettings(fileUuid: string): Promise<boolean> { async function saveSingleFrameSettings(fileUuid: string): Promise<boolean> {
faceFilterSettingsLoading.value = true; settingsLoading.value = true;
faceFilterSettingsError.value = null; settingsError.value = null;
try { try {
const success = await updateFileProfile(fileUuid, faceFilterSettings.value); const success = await updateFileProfile(fileUuid, settings.value);
if (success) { if (success) {
faceFilterSettingsModified.value = false; settingsModified.value = false;
} }
return success; return success;
} catch (err: any) { } catch (err: any) {
faceFilterSettingsError.value = err.message || 'Failed to save settings'; settingsError.value = err.message || 'Failed to save settings';
console.error('Failed to save single frame settings:', err); console.error('Failed to save single frame settings:', err);
return false; return false;
} finally { } finally {
faceFilterSettingsLoading.value = false; settingsLoading.value = false;
} }
} }
@@ -426,7 +414,7 @@ function autoSaveSettings() {
} }
autoSaveTimer = setTimeout(async () => { autoSaveTimer = setTimeout(async () => {
if (currentFileUuid.value && faceFilterSettingsModified.value) { if (currentFileUuid.value && settingsModified.value) {
const success = await saveSingleFrameSettings(currentFileUuid.value); const success = await saveSingleFrameSettings(currentFileUuid.value);
if (!success) { if (!success) {
console.error('Auto-save failed for single frame settings'); console.error('Auto-save failed for single frame settings');
@@ -444,19 +432,18 @@ function cleanupAutoSave() {
} }
function resetSingleFrameSettings() { function resetSingleFrameSettings() {
faceFilterSettings.value = FaceFilterSettingsMigration.defaults(); settings.value = FaceFilterSettingsMigration.defaults();
faceFilterSettingsModified.value = true; settingsModified.value = true;
autoSaveSettings(); autoSaveSettings();
} }
function markSingleFrameSettingsModified() { function markSingleFrameSettingsModified() {
faceFilterSettingsModified.value = true; settingsModified.value = true;
autoSaveSettings(); autoSaveSettings();
} }
return { return {
mode, mode,
rules,
allFaces, allFaces,
filteredFaces, filteredFaces,
filterResult, filterResult,
@@ -464,10 +451,10 @@ return {
loading, loading,
error, error,
stats, stats,
faceFilterSettings, settings,
faceFilterSettingsLoading, settingsLoading,
faceFilterSettingsError, settingsError,
faceFilterSettingsModified, settingsModified,
currentFileUuid, currentFileUuid,
initialize, initialize,

View File

@@ -63,21 +63,10 @@ export interface TraceSettings {
* - 新增字段: max_confidence, max_bbox_size, source, backend_status * - 新增字段: max_confidence, max_bbox_size, source, backend_status
*/ */
export interface FaceFilterSettings { export interface FaceFilterSettings {
version?: string;
ui_filter_rules: UiFilterRules; ui_filter_rules: UiFilterRules;
processing_thresholds: ProcessingThresholds; processing_thresholds: ProcessingThresholds;
trace_settings: TraceSettings; trace_settings: TraceSettings;
min_confidence: number;
max_confidence?: number;
min_bbox_size: number;
max_bbox_size?: number;
source?: string;
backend_status?: string;
single_frame_trace_vlm_enabled: boolean;
single_frame_trace_group_enabled: boolean;
single_frame_trace_unassigned_enabled: boolean;
single_trace_group_filter_enabled: boolean;
} }
/** /**
@@ -142,24 +131,16 @@ export class FaceFilterSettingsMigration {
}; };
return { return {
version: "1.0",
ui_filter_rules: uiFilterRules, ui_filter_rules: uiFilterRules,
processing_thresholds: processingThresholds, processing_thresholds: processingThresholds,
trace_settings: traceSettings, trace_settings: traceSettings,
min_confidence: minConf,
max_confidence: maxConf,
min_bbox_size: legacy.min_bbox_size ?? 80,
max_bbox_size: legacy.max_bbox_size,
source: legacy.source,
backend_status: legacy.backend_status,
single_frame_trace_vlm_enabled: traceSettings.single_frame_trace_vlm_enabled,
single_frame_trace_group_enabled: traceSettings.single_frame_trace_group_enabled,
single_frame_trace_unassigned_enabled: traceSettings.single_frame_trace_unassigned_enabled,
single_trace_group_filter_enabled: traceSettings.single_trace_group_filter_enabled,
}; };
} }
static defaults(): FaceFilterSettings { static defaults(): FaceFilterSettings {
return { return {
version: "1.0",
ui_filter_rules: { ui_filter_rules: {
min_confidence: 0.5, min_confidence: 0.5,
min_bbox_size: 80, min_bbox_size: 80,
@@ -174,12 +155,6 @@ export class FaceFilterSettingsMigration {
single_frame_trace_unassigned_enabled: true, single_frame_trace_unassigned_enabled: true,
single_trace_group_filter_enabled: true, single_trace_group_filter_enabled: true,
}, },
min_confidence: 0.5,
min_bbox_size: 80,
single_frame_trace_vlm_enabled: true,
single_frame_trace_group_enabled: true,
single_frame_trace_unassigned_enabled: true,
single_trace_group_filter_enabled: true,
}; };
} }
} }