feat: Library 批量注册 + PeopleView 修复 + 国际化
Library: - 添加 checkbox 和鼠标框选功能 - Unregistered 卡片添加快速注册按钮 - 状态标签移至左下方,人脸图标移至左侧中间 - Deep Scan 改为 unregister -> register -> process - 完整国际化(日语、繁体中文、英语) PeopleView: - 修复卡片不显示问题(使用 get_file_traces API) - 移除对 identity_id 字段的依赖 其他: - 添加日语语言包 - 添加 useDragSelect composable - 新增文档:Face Bbox Null Bug Report
This commit is contained in:
149
src/composables/useDragSelect.ts
Normal file
149
src/composables/useDragSelect.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { ref, type Ref } from 'vue'
|
||||
|
||||
export interface DragSelectBox {
|
||||
x1: number
|
||||
y1: number
|
||||
x2: number
|
||||
y2: number
|
||||
}
|
||||
|
||||
export interface DragSelectOptions {
|
||||
getSelected: () => string[]
|
||||
setSelected: (keys: string[]) => void
|
||||
keyOf?: (el: HTMLElement) => string
|
||||
onBox?: (box: DragSelectBox | null) => void
|
||||
}
|
||||
|
||||
const DRAG_THRESHOLD = 4
|
||||
|
||||
function overlaps(ax1: number, ay1: number, ax2: number, ay2: number, bx1: number, by1: number, bx2: number, by2: number) {
|
||||
return ax1 < bx2 && ax2 > bx1 && ay1 < by2 && ay2 > by1
|
||||
}
|
||||
|
||||
export function useDragSelect(containerRef: Ref<HTMLElement | null>, opts: DragSelectOptions) {
|
||||
const box = ref<DragSelectBox | null>(null)
|
||||
|
||||
function setBox(b: DragSelectBox | null) {
|
||||
box.value = b
|
||||
opts.onBox?.(b)
|
||||
}
|
||||
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
let curX = 0
|
||||
let curY = 0
|
||||
let moved = false
|
||||
let lastWasDrag = false
|
||||
|
||||
function onMousedown(e: MouseEvent) {
|
||||
if (e.button !== 0) return
|
||||
if (e.ctrlKey || e.metaKey || e.shiftKey) return
|
||||
const target = e.target as HTMLElement
|
||||
if (target.closest('button, a, input, textarea, select, .ms-ppl-card-checkbox, .ms-fm-play-btn')) return
|
||||
|
||||
startX = curX = e.clientX
|
||||
startY = curY = e.clientY
|
||||
moved = false
|
||||
lastWasDrag = false
|
||||
setBox({ x1: startX, y1: startY, x2: startX, y2: startY })
|
||||
|
||||
window.addEventListener('mousemove', onMousemove)
|
||||
window.addEventListener('mouseup', onMouseup)
|
||||
}
|
||||
|
||||
function onMousemove(e: MouseEvent) {
|
||||
curX = e.clientX
|
||||
curY = e.clientY
|
||||
if (!moved && Math.abs(curX - startX) < DRAG_THRESHOLD && Math.abs(curY - startY) < DRAG_THRESHOLD) {
|
||||
setBox({ x1: startX, y1: startY, x2: startX, y2: startY })
|
||||
return
|
||||
}
|
||||
if (!moved) {
|
||||
moved = true
|
||||
document.body.style.userSelect = 'none'
|
||||
}
|
||||
setBox({
|
||||
x1: Math.min(startX, curX),
|
||||
y1: Math.min(startY, curY),
|
||||
x2: Math.max(startX, curX),
|
||||
y2: Math.max(startY, curY),
|
||||
})
|
||||
}
|
||||
|
||||
function onMouseup(e: MouseEvent) {
|
||||
window.removeEventListener('mousemove', onMousemove)
|
||||
window.removeEventListener('mouseup', onMouseup)
|
||||
|
||||
if (!moved) {
|
||||
setBox(null)
|
||||
return
|
||||
}
|
||||
lastWasDrag = true
|
||||
const rect = box.value
|
||||
setBox(null)
|
||||
if (document.body.style.userSelect === 'none') {
|
||||
document.body.style.userSelect = ''
|
||||
}
|
||||
|
||||
const container = containerRef.value
|
||||
if (!container || !rect) {
|
||||
return
|
||||
}
|
||||
|
||||
const bx1 = rect.x1
|
||||
const by1 = rect.y1
|
||||
const bx2 = rect.x2
|
||||
const by2 = rect.y2
|
||||
|
||||
const keyOf = opts.keyOf ?? ((el: HTMLElement) => el.dataset.dragKey || '')
|
||||
const intersecting = new Set<string>()
|
||||
|
||||
const cards = container.querySelectorAll<HTMLElement>('[data-drag-key]')
|
||||
cards.forEach((card) => {
|
||||
const r = card.getBoundingClientRect()
|
||||
if (overlaps(r.left, r.top, r.right, r.bottom, bx1, by1, bx2, by2)) {
|
||||
const key = keyOf(card)
|
||||
if (key) intersecting.add(key)
|
||||
}
|
||||
})
|
||||
|
||||
const prev = opts.getSelected()
|
||||
if (e.shiftKey) {
|
||||
const next = new Set(prev)
|
||||
intersecting.forEach((k) => next.add(k))
|
||||
opts.setSelected(Array.from(next))
|
||||
} else if (e.ctrlKey || e.metaKey) {
|
||||
const next = new Set(prev)
|
||||
intersecting.forEach((k) => {
|
||||
if (next.has(k)) next.delete(k)
|
||||
else next.add(k)
|
||||
})
|
||||
opts.setSelected(Array.from(next))
|
||||
} else {
|
||||
opts.setSelected(Array.from(intersecting))
|
||||
}
|
||||
}
|
||||
|
||||
function onClickCapture(e: MouseEvent) {
|
||||
if (lastWasDrag) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
lastWasDrag = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
box,
|
||||
onMousedown,
|
||||
onClickCapture,
|
||||
}
|
||||
}
|
||||
|
||||
export function dragSelectRectStyle(b: DragSelectBox) {
|
||||
return {
|
||||
left: b.x1 + 'px',
|
||||
top: b.y1 + 'px',
|
||||
width: Math.max(0, b.x2 - b.x1) + 'px',
|
||||
height: Math.max(0, b.y2 - b.y1) + 'px',
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user