fix: increase z-index of face detail navigation arrows
- Navigation arrows z-index increased from 10010 to 10100 - Fixes issue where modal overlay was covering the arrows - Left/right buttons now clickable in face detail modal
This commit is contained in:
@@ -1097,7 +1097,14 @@ const faceMoveTarget = ref('')
|
|||||||
const faceMoveNewGroupName = ref('')
|
const faceMoveNewGroupName = ref('')
|
||||||
|
|
||||||
// Cluster results (synced with store for cross-navigation persistence)
|
// Cluster results (synced with store for cross-navigation persistence)
|
||||||
const clusterResults = computed(() => clusterResultsCache.value[selectedFileUuid.value] || [])
|
const clusterResults = computed(() => {
|
||||||
|
const results = clusterResultsCache.value[selectedFileUuid.value] || []
|
||||||
|
console.log('[clusterResults] cache for', selectedFileUuid.value, ':', results.length, 'groups')
|
||||||
|
if (results.length > 0) {
|
||||||
|
console.log('[clusterResults] first group:', JSON.stringify(results[0], null, 2))
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
})
|
||||||
const clusterTracesMap = ref<Record<string, any>>({})
|
const clusterTracesMap = ref<Record<string, any>>({})
|
||||||
const clusterRunning = computed(() => isClusterRunning(selectedFileUuid.value))
|
const clusterRunning = computed(() => isClusterRunning(selectedFileUuid.value))
|
||||||
|
|
||||||
@@ -1131,6 +1138,10 @@ const faceGroups = computed(() => {
|
|||||||
}
|
}
|
||||||
const defaultName = `group ${String(idx + 1).padStart(2, '0')}`
|
const defaultName = `group ${String(idx + 1).padStart(2, '0')}`
|
||||||
if (!c.name) c.name = defaultName
|
if (!c.name) c.name = defaultName
|
||||||
|
// Convert Person_X to Group_X for display
|
||||||
|
if (c.name && c.name.startsWith('Person_')) {
|
||||||
|
c.name = 'Group_' + c.name.substring(7)
|
||||||
|
}
|
||||||
const visibleTraceIds = ffFilterActive.value
|
const visibleTraceIds = ffFilterActive.value
|
||||||
? traceIds.filter(tid => !ffHiddenTraces.value.has(tid))
|
? traceIds.filter(tid => !ffHiddenTraces.value.has(tid))
|
||||||
: traceIds
|
: traceIds
|
||||||
@@ -1176,11 +1187,19 @@ const clusterDetailCluster = computed(() => {
|
|||||||
// Map trace_id → group name for Faces section
|
// Map trace_id → group name for Faces section
|
||||||
const traceGroupMap = computed(() => {
|
const traceGroupMap = computed(() => {
|
||||||
const map: Record<string, string> = {}
|
const map: Record<string, string> = {}
|
||||||
|
console.log('[traceGroupMap] clusterResults:', clusterResults.value.length, 'groups')
|
||||||
for (const c of clusterResults.value) {
|
for (const c of clusterResults.value) {
|
||||||
|
let name = c.name || ''
|
||||||
|
// Convert Person_X to Group_X for display
|
||||||
|
if (name.startsWith('Person_')) {
|
||||||
|
name = 'Group_' + name.substring(7)
|
||||||
|
}
|
||||||
|
console.log('[traceGroupMap] cluster:', c.cluster_id, 'name:', name, 'trace_ids:', c.trace_ids)
|
||||||
for (const tid of c.trace_ids || []) {
|
for (const tid of c.trace_ids || []) {
|
||||||
map[String(tid)] = c.name
|
map[String(tid)] = name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log('[traceGroupMap] final map:', map)
|
||||||
return map
|
return map
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1218,7 +1237,25 @@ const facesTotalPages = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function traceLabel(tr: any): string {
|
function traceLabel(tr: any): string {
|
||||||
const n = tr?.name || traceGroupMap.value[String(tr.trace_id)]
|
// Prefer cluster name from clusterResultsCache
|
||||||
|
const clusters = clusterResultsCache.value[selectedFileUuid.value] || []
|
||||||
|
console.log('[traceLabel] trace_id:', tr.trace_id, 'clusters in cache:', clusters.length)
|
||||||
|
if (clusters.length > 0) {
|
||||||
|
console.log('[traceLabel] first cluster:', clusters[0])
|
||||||
|
}
|
||||||
|
for (const c of clusters) {
|
||||||
|
if (c.trace_ids && c.trace_ids.includes(tr.trace_id)) {
|
||||||
|
let name = c.name || ''
|
||||||
|
// Convert Person_X to Group_X for display
|
||||||
|
if (name.startsWith('Person_')) {
|
||||||
|
name = 'Group_' + name.substring(7)
|
||||||
|
}
|
||||||
|
console.log('[traceLabel] Found cluster name:', name)
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const n = tr?.name
|
||||||
|
console.log('[traceLabel] Fallback to:', n || t('people.section.unassigned'))
|
||||||
return n || t('people.section.unassigned')
|
return n || t('people.section.unassigned')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2028,6 +2065,11 @@ async function loadClusterResults() {
|
|||||||
}
|
}
|
||||||
const groups = results?.face_groups || results?.clusters || []
|
const groups = results?.face_groups || results?.clusters || []
|
||||||
const frames = results?.frames || []
|
const frames = results?.frames || []
|
||||||
|
console.log('[loadClusterResults] groups:', groups.length, 'frames:', frames.length)
|
||||||
|
if (frames.length > 0) {
|
||||||
|
console.log('[loadClusterResults] first frame faces:', frames[0].faces)
|
||||||
|
console.log('[loadClusterResults] first group:', groups[0])
|
||||||
|
}
|
||||||
// 統一 group_id → cluster_id, 並添加 group_uuid
|
// 統一 group_id → cluster_id, 並添加 group_uuid
|
||||||
for (const g of groups) {
|
for (const g of groups) {
|
||||||
if (g.group_id !== undefined && g.cluster_id === undefined) {
|
if (g.group_id !== undefined && g.cluster_id === undefined) {
|
||||||
@@ -2069,6 +2111,7 @@ async function loadClusterResults() {
|
|||||||
const cid = c.cluster_id || c.group_id
|
const cid = c.cluster_id || c.group_id
|
||||||
c.member_faces = clusterFacesMap[cid] || []
|
c.member_faces = clusterFacesMap[cid] || []
|
||||||
c.face_count = c.member_faces.length
|
c.face_count = c.member_faces.length
|
||||||
|
console.log(`[loadClusterResults] group ${c.name} (cid=${cid}): member_faces=${c.member_faces.length}`)
|
||||||
c.trace_ids = Array.from(clusterTraceIdsMap[cid] || [])
|
c.trace_ids = Array.from(clusterTraceIdsMap[cid] || [])
|
||||||
// Set representative_trace from representative_face
|
// Set representative_trace from representative_face
|
||||||
if (c.representative_face?.face_id) {
|
if (c.representative_face?.face_id) {
|
||||||
@@ -2083,7 +2126,10 @@ async function loadClusterResults() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (groups.length) {
|
if (groups.length) {
|
||||||
|
console.log('[loadClusterResults] Calling setClusterResults with', groups.length, 'groups')
|
||||||
|
console.log('[loadClusterResults] First group trace_ids:', groups[0]?.trace_ids)
|
||||||
setClusterResults(fileUuid, groups)
|
setClusterResults(fileUuid, groups)
|
||||||
|
console.log('[loadClusterResults] After setClusterResults, cache:', clusterResultsCache.value[fileUuid]?.length)
|
||||||
|
|
||||||
// Load trace profiles for names in background (non-blocking)
|
// Load trace profiles for names in background (non-blocking)
|
||||||
loadClusterNames(fileUuid, groups).catch(() => {})
|
loadClusterNames(fileUuid, groups).catch(() => {})
|
||||||
@@ -2749,7 +2795,7 @@ function closeClusterCtxMenu(e?: MouseEvent) {
|
|||||||
clusterCtxMenu.value.show = false
|
clusterCtxMenu.value.show = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function clusterCtxAction(action: string) {
|
async function clusterCtxAction(action: string) {
|
||||||
const c = clusterCtxMenu.value.cluster
|
const c = clusterCtxMenu.value.cluster
|
||||||
if (!c) return
|
if (!c) return
|
||||||
clusterCtxMenu.value.show = false
|
clusterCtxMenu.value.show = false
|
||||||
@@ -2766,7 +2812,7 @@ function clusterCtxAction(action: string) {
|
|||||||
// Update trace profiles via API
|
// Update trace profiles via API
|
||||||
const traceIds = clusters[idx]?.trace_ids || []
|
const traceIds = clusters[idx]?.trace_ids || []
|
||||||
if (traceIds.length && selectedFileUuid.value) {
|
if (traceIds.length && selectedFileUuid.value) {
|
||||||
updateTraceProfileGroup(selectedFileUuid.value, traceIds, { name })
|
await updateTraceProfileGroup(selectedFileUuid.value, traceIds, { name })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (action === 'skip') {
|
} else if (action === 'skip') {
|
||||||
@@ -3297,24 +3343,75 @@ async function executeMerge(targetGroupId: string) {
|
|||||||
const sourceGroupIds = groupIds.filter(id => id !== targetGroupId)
|
const sourceGroupIds = groupIds.filter(id => id !== targetGroupId)
|
||||||
const sourceGroups = faceGroups.value.filter((c: any) => sourceGroupIds.includes(c.group_uuid))
|
const sourceGroups = faceGroups.value.filter((c: any) => sourceGroupIds.includes(c.group_uuid))
|
||||||
|
|
||||||
const sourceGroupNames = sourceGroups.map(g => g.name).filter(Boolean)
|
// Collect all trace_ids from source groups
|
||||||
|
const allTraceIds: number[] = []
|
||||||
|
for (const g of sourceGroups) {
|
||||||
|
if (g.trace_ids) {
|
||||||
|
allTraceIds.push(...g.trace_ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (sourceGroupNames.length === 0) {
|
if (allTraceIds.length === 0) {
|
||||||
alert('No valid source groups to merge')
|
alert('No traces to merge')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!confirm(`Merge ${sourceGroupNames.length} group(s) into "${targetGroupName}"?`)) return
|
if (!confirm(`Merge ${sourceGroups.length} group(s) with ${allTraceIds.length} trace(s) into "${targetGroupName}"?`)) return
|
||||||
|
|
||||||
operationLoading.value = true
|
operationLoading.value = true
|
||||||
operationMessage.value = `Merging ${sourceGroupNames.length} group(s)...`
|
operationMessage.value = `Merging ${sourceGroups.length} group(s)...`
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await apiCall('merge_groups', {
|
// Update all source traces to target group name
|
||||||
fileUuid: selectedFileUuid.value,
|
console.log('[executeMerge] Updating trace_ids:', allTraceIds, 'to name:', targetGroupName)
|
||||||
sourceGroups: sourceGroupNames,
|
|
||||||
targetGroupName: targetGroupName
|
// 1. Update trace_profiles via API
|
||||||
})
|
const result = await updateTraceProfileGroup(selectedFileUuid.value, allTraceIds, { name: targetGroupName })
|
||||||
|
console.log('[executeMerge] API Result:', result)
|
||||||
|
|
||||||
|
// 2. Update clusterResultsCache locally
|
||||||
|
const clusters = getClusterResults(selectedFileUuid.value)
|
||||||
|
|
||||||
|
// Find target cluster
|
||||||
|
const targetCluster = clusters.find((c: any) => c.name === targetGroupName || c.cluster_id === targetGroup.cluster_id)
|
||||||
|
|
||||||
|
if (targetCluster) {
|
||||||
|
// Add source trace_ids to target cluster (avoid duplicates)
|
||||||
|
if (!targetCluster.trace_ids) targetCluster.trace_ids = []
|
||||||
|
for (const tid of allTraceIds) {
|
||||||
|
if (!targetCluster.trace_ids.includes(tid)) {
|
||||||
|
targetCluster.trace_ids.push(tid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetCluster.trace_count = targetCluster.trace_ids.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove trace_ids from source clusters
|
||||||
|
for (const sourceGroup of sourceGroups) {
|
||||||
|
const sourceCluster = clusters.find((c: any) => c.cluster_id === sourceGroup.cluster_id || c.group_id === sourceGroup.group_id)
|
||||||
|
if (sourceCluster && sourceCluster.trace_ids) {
|
||||||
|
sourceCluster.trace_ids = sourceCluster.trace_ids.filter((tid: number) => !allTraceIds.includes(tid))
|
||||||
|
sourceCluster.trace_count = sourceCluster.trace_ids.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove empty source clusters
|
||||||
|
const nonEmptyClusters = clusters.filter((c: any) => !sourceGroups.some(sg =>
|
||||||
|
(c.cluster_id === sg.cluster_id || c.group_id === sg.group_id) && (!c.trace_ids || c.trace_ids.length === 0)
|
||||||
|
))
|
||||||
|
|
||||||
|
setClusterResults(selectedFileUuid.value, nonEmptyClusters)
|
||||||
|
console.log('[executeMerge] Updated cache')
|
||||||
|
|
||||||
|
// Reload thumbnails for updated clusters
|
||||||
|
for (const c of nonEmptyClusters) {
|
||||||
|
if (c.trace_ids && c.trace_ids.length > 0) {
|
||||||
|
const repId = c.representative_trace || c.trace_ids[0]
|
||||||
|
if (repId) {
|
||||||
|
loadTraceThumb({ trace_id: repId, file_uuid: selectedFileUuid.value, best_face_id: 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (result?.success) {
|
if (result?.success) {
|
||||||
@@ -4121,7 +4218,7 @@ h1 { margin: 0; }
|
|||||||
|
|
||||||
/* Face detail side navigation arrows */
|
/* Face detail side navigation arrows */
|
||||||
.ms-fs-nav-arrow {
|
.ms-fs-nav-arrow {
|
||||||
position: fixed; top: 50%; transform: translateY(-50%); z-index: 10010;
|
position: fixed; top: 50%; transform: translateY(-50%); z-index: 10100;
|
||||||
width: 44px; height: 44px; border-radius: 50%; border: none;
|
width: 44px; height: 44px; border-radius: 50%; border: none;
|
||||||
background: rgba(0,0,0,0.5); color: #fff; cursor: pointer;
|
background: rgba(0,0,0,0.5); color: #fff; cursor: pointer;
|
||||||
display: flex; align-items: center; justify-content: center;
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user