Library: - 添加 checkbox 和鼠标框选功能 - Unregistered 卡片添加快速注册按钮 - 状态标签移至左下方,人脸图标移至左侧中间 - Deep Scan 改为 unregister -> register -> process - 完整国际化(日语、繁体中文、英语) PeopleView: - 修复卡片不显示问题(使用 get_file_traces API) - 移除对 identity_id 字段的依赖 其他: - 添加日语语言包 - 添加 useDragSelect composable - 新增文档:Face Bbox Null Bug Report
150 lines
3.8 KiB
TypeScript
150 lines
3.8 KiB
TypeScript
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',
|
|
}
|
|
}
|