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:
@@ -57,7 +57,7 @@
|
||||
<label>Confidence:</label>
|
||||
<input
|
||||
type="number"
|
||||
v-model.number="rules.min_confidence"
|
||||
v-model.number="settings.ui_filter_rules.min_confidence"
|
||||
@change="onRuleChange"
|
||||
min="0"
|
||||
max="1"
|
||||
@@ -72,7 +72,7 @@
|
||||
<label>Bbox Size:</label>
|
||||
<input
|
||||
type="number"
|
||||
v-model.number="rules.min_bbox_size"
|
||||
v-model.number="settings.ui_filter_rules.min_bbox_size"
|
||||
@change="onRuleChange"
|
||||
min="0"
|
||||
step="10"
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
<div class="filter-row">
|
||||
<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="apple_vision">Apple Vision</option>
|
||||
<option value="mediapipe">MediaPipe</option>
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
<div class="filter-row">
|
||||
<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="accept">Accept</option>
|
||||
<option value="review">Review</option>
|
||||
@@ -108,7 +108,7 @@
|
||||
<label class="checkbox-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="faceFilterSettings.single_frame_trace_vlm_enabled"
|
||||
v-model="settings.trace_settings.single_frame_trace_vlm_enabled"
|
||||
@change="markModified"
|
||||
/>
|
||||
VLM Processing
|
||||
@@ -120,7 +120,7 @@
|
||||
<label class="checkbox-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="faceFilterSettings.single_frame_trace_group_enabled"
|
||||
v-model="settings.trace_settings.single_frame_trace_group_enabled"
|
||||
@change="markModified"
|
||||
/>
|
||||
Face Groups
|
||||
@@ -132,7 +132,7 @@
|
||||
<label class="checkbox-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="faceFilterSettings.single_frame_trace_unassigned_enabled"
|
||||
v-model="settings.trace_settings.single_frame_trace_unassigned_enabled"
|
||||
@change="markModified"
|
||||
/>
|
||||
Unassigned List
|
||||
@@ -144,7 +144,7 @@
|
||||
<label class="checkbox-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="faceFilterSettings.single_trace_group_filter_enabled"
|
||||
v-model="settings.trace_settings.single_trace_group_filter_enabled"
|
||||
@change="markModified"
|
||||
/>
|
||||
Single Trace Groups
|
||||
@@ -154,19 +154,19 @@
|
||||
|
||||
<div class="section-actions">
|
||||
<div class="auto-save-hint">
|
||||
{{ faceFilterSettingsLoading ? '⏳ Saving...' : '✓ Auto-saved' }}
|
||||
{{ settingsLoading ? '⏳ Saving...' : '✓ Auto-saved' }}
|
||||
</div>
|
||||
<button
|
||||
class="action-btn"
|
||||
@click="handleResetSingleFrame"
|
||||
:disabled="faceFilterSettingsLoading"
|
||||
:disabled="settingsLoading"
|
||||
>
|
||||
Reset to Default
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="faceFilterSettingsError" class="error-message">
|
||||
❌ {{ faceFilterSettingsError }}
|
||||
<div v-if="settingsError" class="error-message">
|
||||
❌ {{ settingsError }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -232,7 +232,6 @@ const emit = defineEmits<{
|
||||
|
||||
const {
|
||||
mode,
|
||||
rules,
|
||||
filterResult,
|
||||
filterStats,
|
||||
loading,
|
||||
@@ -242,14 +241,14 @@ const {
|
||||
updateRules,
|
||||
applyBackendFilter,
|
||||
resetToDefault,
|
||||
faceFilterSettings,
|
||||
faceFilterSettingsLoading,
|
||||
faceFilterSettingsError,
|
||||
faceFilterSettingsModified,
|
||||
settings,
|
||||
settingsLoading,
|
||||
settingsError,
|
||||
settingsModified,
|
||||
loadSingleFrameSettings,
|
||||
resetSingleFrameSettings,
|
||||
markSingleFrameSettingsModified,
|
||||
cleanupAutoSave
|
||||
cleanupAutoSave,
|
||||
} = useFaceFilter();
|
||||
|
||||
const showPanel = ref(false);
|
||||
@@ -259,7 +258,7 @@ function togglePanel() {
|
||||
}
|
||||
|
||||
function onRuleChange() {
|
||||
updateRules(rules.value);
|
||||
updateRules(settings.value.ui_filter_rules);
|
||||
}
|
||||
|
||||
async function handleApplyBackend() {
|
||||
@@ -291,7 +290,7 @@ watch(showPanel, (show) => {
|
||||
}
|
||||
});
|
||||
|
||||
watch(faceFilterSettingsModified, (newVal, oldVal) => {
|
||||
watch(settingsModified, (newVal, oldVal) => {
|
||||
if (oldVal === true && newVal === false) {
|
||||
emit('settings-updated');
|
||||
}
|
||||
|
||||
@@ -97,13 +97,6 @@ export interface FileProfileResponse {
|
||||
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';
|
||||
|
||||
// API helpers
|
||||
@@ -196,7 +189,7 @@ function hybridFilter(faces: FaceMetadata[], rules: UiFilterRules): FaceMetadata
|
||||
|
||||
export function useFaceFilter() {
|
||||
const mode = ref<'instant' | 'backend' | 'hybrid'>('instant');
|
||||
const rules = ref<UiFilterRules>({ ...defaultRules });
|
||||
const settings = ref<FaceFilterSettings>(FaceFilterSettingsMigration.defaults());
|
||||
const allFaces = ref<FaceMetadata[]>([]);
|
||||
const filteredFaces = ref<FaceMetadata[]>([]);
|
||||
const filterResult = ref<FilterResult | null>(null);
|
||||
@@ -204,10 +197,9 @@ export function useFaceFilter() {
|
||||
const loading = ref(false);
|
||||
const error = ref<string | null>(null);
|
||||
|
||||
const faceFilterSettings = ref<FaceFilterSettings>(FaceFilterSettingsMigration.defaults());
|
||||
const faceFilterSettingsLoading = ref(false);
|
||||
const faceFilterSettingsError = ref<string | null>(null);
|
||||
const faceFilterSettingsModified = ref(false);
|
||||
const settingsLoading = ref(false);
|
||||
const settingsError = ref<string | null>(null);
|
||||
const settingsModified = ref(false);
|
||||
|
||||
const currentFileUuid = ref<string | null>(null);
|
||||
let autoSaveTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
@@ -219,26 +211,24 @@ export function useFaceFilter() {
|
||||
removed: allFaces.value.length - filteredFaces.value.length
|
||||
}));
|
||||
|
||||
// Load preferences from localStorage
|
||||
function loadPreferences() {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
const prefs = JSON.parse(stored);
|
||||
mode.value = prefs.mode || 'instant';
|
||||
rules.value = prefs.rules || { ...defaultRules };
|
||||
settings.value = prefs.settings || FaceFilterSettingsMigration.defaults();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load preferences:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Save preferences to localStorage
|
||||
function savePreferences() {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
||||
mode: mode.value,
|
||||
rules: rules.value
|
||||
settings: settings.value
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error('Failed to save preferences:', err);
|
||||
@@ -297,11 +287,10 @@ export function useFaceFilter() {
|
||||
});
|
||||
}
|
||||
|
||||
// Apply local filter based on mode
|
||||
function applyLocalFilter() {
|
||||
switch (mode.value) {
|
||||
case 'instant':
|
||||
filteredFaces.value = quickFilter(allFaces.value, rules.value);
|
||||
filteredFaces.value = quickFilter(allFaces.value, settings.value.ui_filter_rules);
|
||||
break;
|
||||
case 'backend':
|
||||
filteredFaces.value = allFaces.value.filter(face =>
|
||||
@@ -309,13 +298,13 @@ export function useFaceFilter() {
|
||||
);
|
||||
break;
|
||||
case 'hybrid':
|
||||
filteredFaces.value = hybridFilter(allFaces.value, rules.value);
|
||||
filteredFaces.value = hybridFilter(allFaces.value, settings.value.ui_filter_rules);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function updateRules(newRules: Partial<UiFilterRules>) {
|
||||
rules.value = { ...rules.value, ...newRules };
|
||||
settings.value.ui_filter_rules = { ...settings.value.ui_filter_rules, ...newRules };
|
||||
applyLocalFilter();
|
||||
savePreferences();
|
||||
}
|
||||
@@ -371,52 +360,51 @@ function updateRules(newRules: Partial<UiFilterRules>) {
|
||||
}
|
||||
}
|
||||
|
||||
// Reset to default
|
||||
function resetToDefault() {
|
||||
rules.value = { ...defaultRules };
|
||||
settings.value = FaceFilterSettingsMigration.defaults();
|
||||
applyLocalFilter();
|
||||
savePreferences();
|
||||
}
|
||||
|
||||
async function loadSingleFrameSettings(fileUuid: string) {
|
||||
faceFilterSettingsLoading.value = true;
|
||||
faceFilterSettingsError.value = null;
|
||||
settingsLoading.value = true;
|
||||
settingsError.value = null;
|
||||
|
||||
try {
|
||||
currentFileUuid.value = fileUuid;
|
||||
const profile = await getFileProfile(fileUuid);
|
||||
|
||||
if (profile.face_filter) {
|
||||
faceFilterSettings.value = profile.face_filter;
|
||||
settings.value = profile.face_filter;
|
||||
} else {
|
||||
faceFilterSettings.value = FaceFilterSettingsMigration.defaults();
|
||||
settings.value = FaceFilterSettingsMigration.defaults();
|
||||
}
|
||||
|
||||
faceFilterSettingsModified.value = false;
|
||||
settingsModified.value = false;
|
||||
} 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);
|
||||
} finally {
|
||||
faceFilterSettingsLoading.value = false;
|
||||
settingsLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSingleFrameSettings(fileUuid: string): Promise<boolean> {
|
||||
faceFilterSettingsLoading.value = true;
|
||||
faceFilterSettingsError.value = null;
|
||||
settingsLoading.value = true;
|
||||
settingsError.value = null;
|
||||
|
||||
try {
|
||||
const success = await updateFileProfile(fileUuid, faceFilterSettings.value);
|
||||
const success = await updateFileProfile(fileUuid, settings.value);
|
||||
if (success) {
|
||||
faceFilterSettingsModified.value = false;
|
||||
settingsModified.value = false;
|
||||
}
|
||||
return success;
|
||||
} 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);
|
||||
return false;
|
||||
} finally {
|
||||
faceFilterSettingsLoading.value = false;
|
||||
settingsLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +414,7 @@ function autoSaveSettings() {
|
||||
}
|
||||
|
||||
autoSaveTimer = setTimeout(async () => {
|
||||
if (currentFileUuid.value && faceFilterSettingsModified.value) {
|
||||
if (currentFileUuid.value && settingsModified.value) {
|
||||
const success = await saveSingleFrameSettings(currentFileUuid.value);
|
||||
if (!success) {
|
||||
console.error('Auto-save failed for single frame settings');
|
||||
@@ -444,19 +432,18 @@ function cleanupAutoSave() {
|
||||
}
|
||||
|
||||
function resetSingleFrameSettings() {
|
||||
faceFilterSettings.value = FaceFilterSettingsMigration.defaults();
|
||||
faceFilterSettingsModified.value = true;
|
||||
settings.value = FaceFilterSettingsMigration.defaults();
|
||||
settingsModified.value = true;
|
||||
autoSaveSettings();
|
||||
}
|
||||
|
||||
function markSingleFrameSettingsModified() {
|
||||
faceFilterSettingsModified.value = true;
|
||||
settingsModified.value = true;
|
||||
autoSaveSettings();
|
||||
}
|
||||
|
||||
return {
|
||||
mode,
|
||||
rules,
|
||||
allFaces,
|
||||
filteredFaces,
|
||||
filterResult,
|
||||
@@ -464,10 +451,10 @@ return {
|
||||
loading,
|
||||
error,
|
||||
stats,
|
||||
faceFilterSettings,
|
||||
faceFilterSettingsLoading,
|
||||
faceFilterSettingsError,
|
||||
faceFilterSettingsModified,
|
||||
settings,
|
||||
settingsLoading,
|
||||
settingsError,
|
||||
settingsModified,
|
||||
currentFileUuid,
|
||||
|
||||
initialize,
|
||||
|
||||
@@ -63,21 +63,10 @@ export interface TraceSettings {
|
||||
* - 新增字段: max_confidence, max_bbox_size, source, backend_status
|
||||
*/
|
||||
export interface FaceFilterSettings {
|
||||
version?: string;
|
||||
ui_filter_rules: UiFilterRules;
|
||||
processing_thresholds: ProcessingThresholds;
|
||||
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 {
|
||||
version: "1.0",
|
||||
ui_filter_rules: uiFilterRules,
|
||||
processing_thresholds: processingThresholds,
|
||||
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 {
|
||||
return {
|
||||
version: "1.0",
|
||||
ui_filter_rules: {
|
||||
min_confidence: 0.5,
|
||||
min_bbox_size: 80,
|
||||
@@ -174,12 +155,6 @@ export class FaceFilterSettingsMigration {
|
||||
single_frame_trace_unassigned_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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user