feat(shortcut): ctrl+f
This commit is contained in:
@@ -1376,6 +1376,141 @@ const LabelPage = ({
|
||||
[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(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
// console.log(e, e.key);
|
||||
@@ -1384,6 +1519,18 @@ const LabelPage = ({
|
||||
console.log(e.key)
|
||||
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") {
|
||||
e.preventDefault()
|
||||
// console.log(e.key);
|
||||
@@ -1601,6 +1748,7 @@ const LabelPage = ({
|
||||
editMode,
|
||||
focusInput,
|
||||
getOperationSchema,
|
||||
handleMergeSelectedObjects,
|
||||
handleShortcut,
|
||||
isView,
|
||||
projectDetail?.label_type,
|
||||
|
||||
@@ -1638,6 +1638,7 @@ const TopTools = (
|
||||
useImperativeHandle(ref, () => ({
|
||||
handleSave,
|
||||
handleBackup,
|
||||
commitTaskByActionKey,
|
||||
}))
|
||||
|
||||
const renderEditIcon = useMemo(() => {
|
||||
|
||||
Reference in New Issue
Block a user