feat(shortcut): ctrl+f

This commit is contained in:
zhangheng
2026-04-03 16:59:44 +08:00
parent 0eb18943a4
commit b6ae2a6a29
2 changed files with 149 additions and 0 deletions

View File

@@ -1376,6 +1376,141 @@ const LabelPage = ({
[getOperationSchema, setActiveOperation] [getOperationSchema, setActiveOperation]
) )
const handleMergeSelectedObjects = useCallback(() => {
const activeImage = useBottomToolsStore.getState().activeImage
const selectedIds =
useObjectStore.getState().selectedPath[activeImage] || []
if (selectedIds.length <= 1) {
notifications.show({
color: "yellow",
message: "请先选中同类别且数量大于1的对象",
})
return
}
const nextLabel = safeClone(useLabelStore.getState().label)
const imageLabel = nextLabel.get(activeImage)
if (!imageLabel) return
const selectedIdSet = new Set(selectedIds)
const selectedByOperation = Array.from(imageLabel.entries())
.map(([operationId, paths]) => {
const selectedPaths = paths.filter((item) => selectedIdSet.has(item[0]))
return { operationId, selectedPaths }
})
.filter((item) => item.selectedPaths.length > 0)
if (
selectedByOperation.length !== 1 ||
selectedByOperation[0].selectedPaths.length <= 1 ||
selectedByOperation[0].selectedPaths.length !== selectedIds.length
) {
notifications.show({
color: "yellow",
message: "仅支持合并同类别且数量大于1的选中对象",
})
return
}
const { operationId, selectedPaths } = selectedByOperation[0]
const selectedTypeList = selectedIds
.map((id) => usePaperStore.getState().getItemById(id)?.data?.type)
.filter((type): type is string => typeof type === "string")
const selectedTypeSet = new Set(selectedTypeList)
const selectedType = Array.from(selectedTypeSet)[0]
const operationSchema = getOperationSchema(operationId)
const operationType = operationSchema
? labelTypeMap.get(operationSchema.label_type)
: ""
if (
selectedTypeList.length !== selectedIds.length ||
selectedTypeSet.size !== 1 ||
!["polygon", "brush"].includes(selectedType) ||
!["多边形", "多线段"].includes(operationType || "")
) {
notifications.show({
color: "yellow",
message: "仅支持合并 polygon/brush 类型对象",
})
return
}
const sortedSelectedPaths = [...selectedPaths].sort((a, b) => a[0] - b[0])
const targetId = sortedSelectedPaths[0][0]
const mergedPaths = sortedSelectedPaths.flatMap((item) =>
safeClone(item[1] || [])
)
const mergedCompoundPaths = sortedSelectedPaths.flatMap((item) =>
safeClone(item[3] || [])
)
const mergedDetail = {
...(safeClone(sortedSelectedPaths[0][2]) || {}),
parentGroupId: null,
}
const mergedData: [number, any[], any, any[]] = [
targetId,
mergedPaths,
mergedDetail,
mergedCompoundPaths,
]
const restPaths = (imageLabel.get(operationId) || []).filter(
(item) => !selectedIdSet.has(item[0])
)
imageLabel.set(
operationId,
[...restPaths, mergedData].sort((a, b) => a[0] - b[0])
)
const nextPathGroupMap = safeClone(
useRightToolsStore.getState().pathGroupMap
)
const imageGroupMap = nextPathGroupMap.get(activeImage)
if (imageGroupMap) {
for (let [groupId, pathIds] of imageGroupMap.entries()) {
const nextPathIds = pathIds.filter((id) => !selectedIdSet.has(id))
if (nextPathIds.length > 1) {
imageGroupMap.set(groupId, nextPathIds)
} else {
imageGroupMap.delete(groupId)
}
}
if (!imageGroupMap.size) nextPathGroupMap.delete(activeImage)
useRightToolsStore.getState().setPathGroupMap(nextPathGroupMap)
}
useLabelStore.getState().setLabel(nextLabel)
useLabelStore.getState().pushStateStack(nextLabel)
useObjectStore.setState((state) => ({
...state,
selectedPath: {
...state.selectedPath,
[activeImage]: [targetId],
},
}))
const paperGroup = usePaperStore.getState().group
if (paperGroup) {
paperGroup
.getItems({})
.filter((item) => item.data.id)
.forEach((item) => {
item.remove()
})
for (const [categoryId] of imageLabel.entries()) {
paperContainerRef.current?.renderPolygons?.(categoryId, imageLabel)
}
}
notifications.show({
color: "green",
message: `已合并${selectedIds.length}个对象`,
})
}, [getOperationSchema])
useEffect(() => { useEffect(() => {
const down = (e: KeyboardEvent) => { const down = (e: KeyboardEvent) => {
// console.log(e, e.key); // console.log(e, e.key);
@@ -1384,6 +1519,18 @@ const LabelPage = ({
console.log(e.key) console.log(e.key)
const mode = usePaperStore.getState().mode const mode = usePaperStore.getState().mode
if (e.altKey && e.key.toLowerCase() === "x") {
e.preventDefault()
if (!isView) topRef.current?.commitTaskByActionKey?.()
return
}
if (e.ctrlKey && e.key.toLowerCase() === "f") {
e.preventDefault()
handleMergeSelectedObjects()
return
}
if (e.key === "Alt" && mode !== "support") { if (e.key === "Alt" && mode !== "support") {
e.preventDefault() e.preventDefault()
// console.log(e.key); // console.log(e.key);
@@ -1601,6 +1748,7 @@ const LabelPage = ({
editMode, editMode,
focusInput, focusInput,
getOperationSchema, getOperationSchema,
handleMergeSelectedObjects,
handleShortcut, handleShortcut,
isView, isView,
projectDetail?.label_type, projectDetail?.label_type,

View File

@@ -1638,6 +1638,7 @@ const TopTools = (
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
handleSave, handleSave,
handleBackup, handleBackup,
commitTaskByActionKey,
})) }))
const renderEditIcon = useMemo(() => { const renderEditIcon = useMemo(() => {