Library: - 添加 checkbox 和鼠标框选功能 - Unregistered 卡片添加快速注册按钮 - 状态标签移至左下方,人脸图标移至左侧中间 - Deep Scan 改为 unregister -> register -> process - 完整国际化(日语、繁体中文、英语) PeopleView: - 修复卡片不显示问题(使用 get_file_traces API) - 移除对 identity_id 字段的依赖 其他: - 添加日语语言包 - 添加 useDragSelect composable - 新增文档:Face Bbox Null Bug Report
255 lines
7.5 KiB
TypeScript
255 lines
7.5 KiB
TypeScript
import { reactive } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { apiCall } from '@/api'
|
|
import { invalidatePeople, ensurePeople } from '@/store'
|
|
|
|
interface HistoryCounts {
|
|
patchUndo: number
|
|
patchRedo: number
|
|
bindUndo: number
|
|
bindRedo: number
|
|
}
|
|
|
|
interface MergeHistoryItem {
|
|
mergeId: string
|
|
sourceUuid: string
|
|
targetUuid: string
|
|
sourceName: string
|
|
targetName: string
|
|
undone: boolean
|
|
createdAt: string
|
|
}
|
|
|
|
interface ActionHistoryItem {
|
|
type: 'patch' | 'bind' | 'merge' | 'delete'
|
|
label: string
|
|
uuid: string
|
|
timestamp: string
|
|
canUndo: boolean
|
|
canRedo: boolean
|
|
}
|
|
|
|
export function useUndoRedo() {
|
|
const { t } = useI18n()
|
|
const counts = reactive<Record<string, HistoryCounts>>({})
|
|
const mergeHistory = reactive<MergeHistoryItem[]>([])
|
|
const recentActions = reactive<ActionHistoryItem[]>([])
|
|
|
|
async function refreshCounts(uuid: string) {
|
|
try {
|
|
const [patchHist, bindHist] = await Promise.all([
|
|
apiCall('identity_history', { uuid, page: 1, pageSize: 1 }),
|
|
apiCall('identity_bind_history', { uuid, page: 1, pageSize: 1 }),
|
|
])
|
|
if (!counts[uuid]) counts[uuid] = { patchUndo: 0, patchRedo: 0, bindUndo: 0, bindRedo: 0 }
|
|
counts[uuid].patchUndo = patchHist?.undo_stack_count ?? patchHist?.undoStackCount ?? 0
|
|
counts[uuid].patchRedo = patchHist?.redo_stack_count ?? patchHist?.redoStackCount ?? 0
|
|
counts[uuid].bindUndo = bindHist?.undo_stack_count ?? bindHist?.undoStackCount ?? 0
|
|
counts[uuid].bindRedo = bindHist?.redo_stack_count ?? bindHist?.redoStackCount ?? 0
|
|
} catch (e) {
|
|
console.error('Failed to refresh undo counts:', e)
|
|
}
|
|
}
|
|
|
|
async function refreshMergeHistory(uuid?: string) {
|
|
try {
|
|
const params: Record<string, any> = { pageSize: 10 }
|
|
if (uuid) params.sourceUuid = uuid
|
|
const data = await apiCall('merge_history', params)
|
|
const items = data?.history || data?.merges || data || []
|
|
mergeHistory.splice(0, mergeHistory.length)
|
|
for (const m of Array.isArray(items) ? items : []) {
|
|
mergeHistory.push({
|
|
mergeId: m.merge_id ?? m.mergeId ?? '',
|
|
sourceUuid: m.source_uuid ?? m.sourceUuid ?? '',
|
|
targetUuid: m.target_uuid ?? m.targetUuid ?? '',
|
|
sourceName: m.source_name ?? m.sourceName ?? '',
|
|
targetName: m.target_name ?? m.targetName ?? '',
|
|
undone: m.is_undone ?? m.isUndone ?? false,
|
|
createdAt: m.created_at ?? m.createdAt ?? '',
|
|
})
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to refresh merge history:', e)
|
|
}
|
|
}
|
|
|
|
async function refreshActionHistory(uuid: string) {
|
|
try {
|
|
const [patchHist, bindHist] = await Promise.all([
|
|
apiCall('identity_history', { uuid, page: 1, pageSize: 10 }),
|
|
apiCall('identity_bind_history', { uuid, page: 1, pageSize: 10 }),
|
|
])
|
|
const actions: ActionHistoryItem[] = []
|
|
const patchItems = patchHist?.history || patchHist?.records || []
|
|
for (const r of Array.isArray(patchItems) ? patchItems : []) {
|
|
const op = r.operation ?? r.op ?? ''
|
|
const label = op === 'delete' ? t('people.action.delete_person') : op === 'update' ? t('people.action.update_person') : op
|
|
actions.push({
|
|
type: op === 'delete' ? 'delete' : 'patch',
|
|
label,
|
|
uuid,
|
|
timestamp: r.created_at ?? r.createdAt ?? r.timestamp ?? '',
|
|
canUndo: !r.is_undone && !r.isUndone,
|
|
canRedo: !!(r.is_undone || r.isUndone),
|
|
})
|
|
}
|
|
const bindItems = bindHist?.history || bindHist?.records || []
|
|
for (const r of Array.isArray(bindItems) ? bindItems : []) {
|
|
const op = r.operation ?? r.op ?? ''
|
|
const label = op === 'bind' ? t('people.action.bind_face') : op === 'unbind' ? t('people.action.unbind_face') : op === 'bind_trace' ? t('people.action.bind_trace') : op
|
|
actions.push({
|
|
type: 'bind',
|
|
label,
|
|
uuid,
|
|
timestamp: r.created_at ?? r.createdAt ?? r.timestamp ?? '',
|
|
canUndo: !(r.is_undone ?? r.isUndone),
|
|
canRedo: !!(r.is_undone ?? r.isUndone),
|
|
})
|
|
}
|
|
actions.sort((a, b) => b.timestamp.localeCompare(a.timestamp))
|
|
recentActions.splice(0, recentActions.length, ...actions.slice(0, 20))
|
|
} catch (e) {
|
|
console.error('Failed to refresh action history:', e)
|
|
}
|
|
}
|
|
|
|
async function undo(uuid: string, steps = 1) {
|
|
try {
|
|
await apiCall('identity_undo', { uuid, steps })
|
|
await refreshCounts(uuid)
|
|
await refreshActionHistory(uuid)
|
|
invalidatePeople()
|
|
await ensurePeople()
|
|
} catch (e) {
|
|
console.error('Undo failed:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
async function redo(uuid: string, steps = 1) {
|
|
try {
|
|
await apiCall('identity_redo', { uuid, steps })
|
|
await refreshCounts(uuid)
|
|
await refreshActionHistory(uuid)
|
|
invalidatePeople()
|
|
await ensurePeople()
|
|
} catch (e) {
|
|
console.error('Redo failed:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
async function bindUndo(uuid: string, steps = 1) {
|
|
try {
|
|
await apiCall('identity_bind_undo', { uuid, steps })
|
|
await refreshCounts(uuid)
|
|
await refreshActionHistory(uuid)
|
|
invalidatePeople()
|
|
await ensurePeople()
|
|
} catch (e) {
|
|
console.error('Bind undo failed:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
async function bindRedo(uuid: string, steps = 1) {
|
|
try {
|
|
await apiCall('identity_bind_redo', { uuid, steps })
|
|
await refreshCounts(uuid)
|
|
await refreshActionHistory(uuid)
|
|
invalidatePeople()
|
|
await ensurePeople()
|
|
} catch (e) {
|
|
console.error('Bind redo failed:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
async function mergeUndo(mergeId: string) {
|
|
try {
|
|
await apiCall('merge_undo', { mergeId })
|
|
invalidatePeople()
|
|
await ensurePeople()
|
|
} catch (e) {
|
|
console.error('Merge undo failed:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
async function mergeRedo(mergeId: string) {
|
|
try {
|
|
await apiCall('merge_redo', { mergeId })
|
|
invalidatePeople()
|
|
await ensurePeople()
|
|
} catch (e) {
|
|
console.error('Merge redo failed:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
function canUndo(uuid: string): boolean {
|
|
const c = counts[uuid]
|
|
if (!c) return false
|
|
return c.patchUndo > 0 || c.bindUndo > 0
|
|
}
|
|
|
|
function canRedo(uuid: string): boolean {
|
|
const c = counts[uuid]
|
|
if (!c) return false
|
|
return c.patchRedo > 0 || c.bindRedo > 0
|
|
}
|
|
|
|
async function undoAction(uuid: string, type: 'patch' | 'bind') {
|
|
try {
|
|
if (type === 'bind') {
|
|
await apiCall('identity_bind_undo', { uuid, steps: 1 })
|
|
} else {
|
|
await apiCall('identity_undo', { uuid, steps: 1 })
|
|
}
|
|
await refreshCounts(uuid)
|
|
await refreshActionHistory(uuid)
|
|
invalidatePeople()
|
|
await ensurePeople()
|
|
} catch (e) {
|
|
console.error('Undo action failed:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
async function redoAction(uuid: string, type: 'patch' | 'bind') {
|
|
try {
|
|
if (type === 'bind') {
|
|
await apiCall('identity_bind_redo', { uuid, steps: 1 })
|
|
} else {
|
|
await apiCall('identity_redo', { uuid, steps: 1 })
|
|
}
|
|
await refreshCounts(uuid)
|
|
await refreshActionHistory(uuid)
|
|
invalidatePeople()
|
|
await ensurePeople()
|
|
} catch (e) {
|
|
console.error('Redo action failed:', e)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
return {
|
|
counts,
|
|
mergeHistory,
|
|
recentActions,
|
|
refreshCounts,
|
|
refreshMergeHistory,
|
|
refreshActionHistory,
|
|
undo,
|
|
redo,
|
|
bindUndo,
|
|
bindRedo,
|
|
mergeUndo,
|
|
mergeRedo,
|
|
canUndo,
|
|
canRedo,
|
|
undoAction,
|
|
redoAction,
|
|
}
|
|
} |