fix(tag): bug

This commit is contained in:
zhangheng
2026-04-16 16:36:24 +08:00
parent 8b89c37a1a
commit fca9430246
4 changed files with 121 additions and 49 deletions

View File

@@ -166,6 +166,24 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
[activeImage, continueObjectId, storeLabel] [activeImage, continueObjectId, storeLabel]
) )
const getObjectItemsById = useCallback(
(id: number) => {
return getItemsById(id).filter((item) => item.data?.type !== "tag")
},
[getItemsById]
)
const bringTagItemsToFront = useCallback(
(id: number) => {
getItemsById(id).forEach((item) => {
if (item.data?.type === "tag") {
item.bringToFront()
}
})
},
[getItemsById]
)
// 切换选中对象时,清除所有其他对象的当前选中状态 // 切换选中对象时,清除所有其他对象的当前选中状态
const clearSelectedItems = () => { const clearSelectedItems = () => {
usePaperStore usePaperStore
@@ -215,11 +233,16 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
const handleSelect = useCallback( const handleSelect = useCallback(
(id: number, operationId: any) => { (id: number, operationId: any) => {
if (pathStatus[activeImage + id]?.HIDE) return
const objectItems = getObjectItemsById(id)
const targetItem = getItemById(id)
if (selectedPath[activeImage]?.length) { if (selectedPath[activeImage]?.length) {
if (!selectedPath[activeImage]?.includes(id)) { if (!selectedPath[activeImage]?.includes(id)) {
if (pressShift) { if (pressShift) {
if (getItemsById(id)?.length) { if (objectItems.length && targetItem) {
getItemsById(id).forEach((item) => { objectItems.forEach((item) => {
if (item instanceof paper.Path) { if (item instanceof paper.Path) {
handleSelectedPath(item) handleSelectedPath(item)
} else if (item instanceof paper.CompoundPath) { } else if (item instanceof paper.CompoundPath) {
@@ -228,13 +251,14 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
else item.data.selected = true else item.data.selected = true
} }
}) })
bringTagItemsToFront(id)
let category = useTopToolsStore let category = useTopToolsStore
.getState() .getState()
.objectOperations.find( .objectOperations.find(
(item) => item.category_id.toString() === operationId (item) => item.category_id.toString() === operationId
) )
const globalPosition = getItemById(id)!.globalMatrix.transform( const globalPosition = targetItem.globalMatrix.transform(
getItemById(id)!.position targetItem.position
) )
const { top, left } = getShowContextMenuPosition( const { top, left } = getShowContextMenuPosition(
globalPosition, globalPosition,
@@ -251,8 +275,9 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
} else { } else {
clearSelectedItems() clearSelectedItems()
selectedPath[activeImage].forEach((selectedId) => { selectedPath[activeImage].forEach((selectedId) => {
if (getItemsById(selectedId)?.length) { const selectedObjectItems = getObjectItemsById(selectedId)
getItemsById(selectedId).forEach((item) => { if (selectedObjectItems.length) {
selectedObjectItems.forEach((item) => {
cancelSelectedPathColor(item) cancelSelectedPathColor(item)
}) })
useOtherToolsStore.getState().setShowContextMenu({ useOtherToolsStore.getState().setShowContextMenu({
@@ -267,8 +292,8 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
}) })
} }
}) })
if (getItemsById(id)?.length) { if (objectItems.length && targetItem) {
getItemsById(id).forEach((item) => { objectItems.forEach((item) => {
if (item instanceof paper.Path) { if (item instanceof paper.Path) {
handleSelectedPath(item) handleSelectedPath(item)
} else if (item instanceof paper.CompoundPath) { } else if (item instanceof paper.CompoundPath) {
@@ -277,13 +302,14 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
else item.data.selected = true else item.data.selected = true
} }
}) })
bringTagItemsToFront(id)
let category = useTopToolsStore let category = useTopToolsStore
.getState() .getState()
.objectOperations.find( .objectOperations.find(
(item) => item.category_id.toString() === operationId (item) => item.category_id.toString() === operationId
) )
const globalPosition = getItemById(id)!.globalMatrix.transform( const globalPosition = targetItem.globalMatrix.transform(
getItemById(id)!.position targetItem.position
) )
const { top, left } = getShowContextMenuPosition( const { top, left } = getShowContextMenuPosition(
globalPosition, globalPosition,
@@ -302,8 +328,8 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
} else { } else {
clearSelectedItems() clearSelectedItems()
updateSelectedPath(activeImage, "DELETE", id) updateSelectedPath(activeImage, "DELETE", id)
if (getItemsById(id)?.length) { if (objectItems.length) {
getItemsById(id).forEach((item) => { objectItems.forEach((item) => {
cancelSelectedPathColor(item) cancelSelectedPathColor(item)
}) })
useOtherToolsStore.getState().setShowContextMenu({ useOtherToolsStore.getState().setShowContextMenu({
@@ -321,8 +347,8 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
} else { } else {
clearSelectedItems() clearSelectedItems()
updateSelectedPath(activeImage, "ADD", id) updateSelectedPath(activeImage, "ADD", id)
if (getItemsById(id)?.length) { if (objectItems.length && targetItem) {
getItemsById(id).forEach((item) => { objectItems.forEach((item) => {
if (item instanceof paper.Path) { if (item instanceof paper.Path) {
handleSelectedPath(item) handleSelectedPath(item)
} else if (item instanceof paper.CompoundPath) { } else if (item instanceof paper.CompoundPath) {
@@ -337,14 +363,15 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
// item.fillColor = item.data?.fillColor; // item.fillColor = item.data?.fillColor;
// item.bringToFront(); // item.bringToFront();
}) })
bringTagItemsToFront(id)
let category = useTopToolsStore let category = useTopToolsStore
.getState() .getState()
.objectOperations.find( .objectOperations.find(
(item) => item.category_id.toString() === operationId (item) => item.category_id.toString() === operationId
) )
const globalPosition = getItemById(id)!.globalMatrix.transform( const globalPosition = targetItem.globalMatrix.transform(
getItemById(id)!.position targetItem.position
) )
const { top, left } = getShowContextMenuPosition( const { top, left } = getShowContextMenuPosition(
globalPosition, globalPosition,
@@ -362,8 +389,10 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
}, },
[ [
activeImage, activeImage,
bringTagItemsToFront,
getObjectItemsById,
getItemById, getItemById,
getItemsById, pathStatus,
pressShift, pressShift,
selectedPath, selectedPath,
updateSelectedPath, updateSelectedPath,
@@ -889,10 +918,13 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
: "var(--mantine-color-gray-0)" : "var(--mantine-color-gray-0)"
} }
c={isSelected ? "white" : undefined} c={isSelected ? "white" : undefined}
onClick={() => onClick={() => {
if (isChildHide) return
handleSelect(child[0], operationId) handleSelect(child[0], operationId)
} }}
style={{ cursor: "pointer" }}> style={{
cursor: isChildHide ? "default" : "pointer",
}}>
<Flex justify="space-between" align="center"> <Flex justify="space-between" align="center">
<Group gap="xs"> <Group gap="xs">
<ActionIcon <ActionIcon

View File

@@ -20,6 +20,7 @@ import { usePaperSupportStore } from "./usePaperSupportStore"
import { useRightToolsStore } from "./useRightToolsStore" import { useRightToolsStore } from "./useRightToolsStore"
import { DEFAULT_NODE_SIZE, useTopToolsStore } from "./useTopToolsStore" import { DEFAULT_NODE_SIZE, useTopToolsStore } from "./useTopToolsStore"
import { safeClone } from "./utils/clone" import { safeClone } from "./utils/clone"
import { getObjectTagVisibility } from "./utils/objectVisibility"
interface ContinuePolygonTarget { interface ContinuePolygonTarget {
imageId: string imageId: string
@@ -216,6 +217,16 @@ const initialPaperState = {
reciprocalRasterScale: {}, reciprocalRasterScale: {},
resizeGhostRequestToken: 0, resizeGhostRequestToken: 0,
} }
const insertItemBelowTags = (item: paper.Item | null | undefined) => {
if (!item) return
const group = usePaperStore.getState().group
if (!group || item.parent !== group) return
const firstTagItem = group.getItems({ data: { type: "tag" } })[0]
if (!firstTagItem || firstTagItem === item) return
item.insertBelow(firstTagItem)
}
const handleDelete = () => { const handleDelete = () => {
const group = usePaperStore.getState().group! const group = usePaperStore.getState().group!
const imageId = useBottomToolsStore.getState().activeImage const imageId = useBottomToolsStore.getState().activeImage
@@ -450,12 +461,7 @@ const clearPaperSelectedObjects = () => {
.getState() .getState()
.getItemsById(id) .getItemsById(id)
.forEach((item) => { .forEach((item) => {
if ( resetPaperShapeFillColor(item)
(item instanceof paper.Path || item instanceof paper.CompoundPath) &&
["rectangle", "polygon"].includes(item.data?.type || "")
) {
item.fillColor = item.data?.blankColor || item.fillColor || null
}
}) })
}) })
@@ -464,6 +470,16 @@ const clearPaperSelectedObjects = () => {
}) })
} }
const resetPaperShapeFillColor = (item: paper.Item) => {
if (!(item instanceof paper.Path || item instanceof paper.CompoundPath)) {
return
}
if (!["rectangle", "polygon"].includes(item.data?.type || "")) {
return
}
item.fillColor = item.data?.blankColor || item.fillColor || null
}
const applyPaperSelectedPath = (item: paper.Path | paper.CompoundPath) => { const applyPaperSelectedPath = (item: paper.Path | paper.CompoundPath) => {
if (item.data.type === "rectangle" && item instanceof paper.Path) { if (item.data.type === "rectangle" && item instanceof paper.Path) {
item.data.selected = true item.data.selected = true
@@ -2011,15 +2027,14 @@ export const usePaperStore = create<PaperState>((set) => ({
type: "tag", type: "tag",
}, },
}) })
// 标识位为true时选中路径隐藏tags; 没选中显示tags // 仅恢复当前应显示的标签,隐藏对象的标签保持不可见
const configs = useTopToolsStore.getState().showTagsConfigs
if (useTopToolsStore.getState().showTags) { if (useTopToolsStore.getState().showTags) {
const setTagVisibleByConfig = (tag: any) => { const setTagVisibleByConfig = (tag: any) => {
if (tag.data.tag_category === "mask") tag.visible = getObjectTagVisibility(
tag.visible = configs.includes("外框") useBottomToolsStore.getState().activeImage,
else if (tag.data.tag_category === "point_text") Number(tag.data?.id),
tag.visible = configs.includes("点ID") tag.data?.tag_category
else tag.visible = true )
} }
lastTags.forEach(setTagVisibleByConfig) lastTags.forEach(setTagVisibleByConfig)
tags.forEach(setTagVisibleByConfig) tags.forEach(setTagVisibleByConfig)
@@ -3147,6 +3162,7 @@ export const usePaperStore = create<PaperState>((set) => ({
}) })
) )
polygon.data = Object.assign({ type: "polygon", ...option }, data) polygon.data = Object.assign({ type: "polygon", ...option }, data)
insertItemBelowTags(polygon)
points?.forEach((point) => { points?.forEach((point) => {
polygon.add(point) polygon.add(point)
}) })
@@ -3219,6 +3235,7 @@ export const usePaperStore = create<PaperState>((set) => ({
}) })
) )
compoundPath.data = Object.assign({ type: "polygon", ...option }, data) compoundPath.data = Object.assign({ type: "polygon", ...option }, data)
insertItemBelowTags(compoundPath)
compoundPath.onMouseDown = (e: { compoundPath.onMouseDown = (e: {
event: MouseEvent event: MouseEvent
point: paper.Point point: paper.Point
@@ -4606,8 +4623,7 @@ export const usePaperStore = create<PaperState>((set) => ({
.getState() .getState()
.group!.getItems({}) .group!.getItems({})
.forEach((item) => { .forEach((item) => {
// clear color resetPaperShapeFillColor(item)
item.fillColor = item?.data?.blankColor
}) })
} }
@@ -4803,6 +4819,7 @@ export const usePaperStore = create<PaperState>((set) => ({
Object.assign(option, { strokeScaling: false }) Object.assign(option, { strokeScaling: false })
) )
rect.data = Object.assign({ type: "rectangle", ...option }, data) rect.data = Object.assign({ type: "rectangle", ...option }, data)
insertItemBelowTags(rect)
rect.onMouseEnter = (_e: paper.MouseEvent) => { rect.onMouseEnter = (_e: paper.MouseEvent) => {
// if (usePaperStore.getState().mode === "pan") { // if (usePaperStore.getState().mode === "pan") {
// e.target.strokeWidth += 2; // e.target.strokeWidth += 2;
@@ -5045,6 +5062,7 @@ export const usePaperStore = create<PaperState>((set) => ({
) )
) )
brush.data = Object.assign({ type: "brush", ...option }, data) brush.data = Object.assign({ type: "brush", ...option }, data)
insertItemBelowTags(brush)
// points?.forEach((point) => { // points?.forEach((point) => {
// brush.add(point); // brush.add(point);
// }); // });
@@ -5545,6 +5563,7 @@ export const usePaperStore = create<PaperState>((set) => ({
const newScale = useTopToolsStore.getState().scale / currentScaling const newScale = useTopToolsStore.getState().scale / currentScaling
myCircle.scale(newScale / currentScaling) myCircle.scale(newScale / currentScaling)
myCircle.data = Object.assign({ type: "circle", ...option }, data) myCircle.data = Object.assign({ type: "circle", ...option }, data)
insertItemBelowTags(myCircle)
syncPaperCircleDiameter(myCircle) syncPaperCircleDiameter(myCircle)
myCircle.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => { myCircle.onMouseDown = (e: { event: MouseEvent; point: paper.Point }) => {
if (e.event && e.event.button === 2) { if (e.event && e.event.button === 2) {
@@ -5614,6 +5633,7 @@ export const usePaperStore = create<PaperState>((set) => ({
) )
) )
myCircleLine.data = Object.assign({ type: "point", ...option }, data) myCircleLine.data = Object.assign({ type: "point", ...option }, data)
insertItemBelowTags(myCircleLine)
return myCircleLine return myCircleLine
}, },

View File

@@ -4,6 +4,7 @@ import { useLabelStore } from "./store"
import { useBottomToolsStore } from "./useBottomToolsStore" import { useBottomToolsStore } from "./useBottomToolsStore"
import { usePaperStore } from "./usePaperStore" import { usePaperStore } from "./usePaperStore"
import { useTopToolsStore } from "./useTopToolsStore" import { useTopToolsStore } from "./useTopToolsStore"
import { getObjectTagVisibility } from "./utils/objectVisibility"
const TAG_FONT_SIZE = 12 const TAG_FONT_SIZE = 12
const TAG_VERTICAL_GAP = 8 const TAG_VERTICAL_GAP = 8
@@ -159,13 +160,12 @@ const useRenderTag = () => {
const activeImage = useBottomToolsStore.getState().activeImage const activeImage = useBottomToolsStore.getState().activeImage
const group = usePaperStore.getState().group const group = usePaperStore.getState().group
if (!group) return if (!group) return
const visible = useTopToolsStore.getState().showTags
const configs = useTopToolsStore.getState().showTagsConfigs const configs = useTopToolsStore.getState().showTagsConfigs
const groupScale = const groupScale =
Number.isFinite(group.scaling?.x) && group.scaling.x !== 0 Number.isFinite(group.scaling?.x) && group.scaling.x !== 0
? group.scaling.x ? group.scaling.x
: 1 : 1
const applyPointTagCompensation = ( const applyScreenSpaceTagCompensation = (
item: paper.Item, item: paper.Item,
anchor: [number, number] anchor: [number, number]
) => { ) => {
@@ -235,7 +235,7 @@ const useRenderTag = () => {
tag_anchor: tagAnchor, tag_anchor: tagAnchor,
}, },
parent: group, parent: group,
visible, visible: getObjectTagVisibility(activeImage, id, "text"),
}) })
text.translate( text.translate(
new paper.Point( new paper.Point(
@@ -259,8 +259,10 @@ const useRenderTag = () => {
tag_anchor: tagAnchor, tag_anchor: tagAnchor,
}, },
parent: group, parent: group,
visible, visible: getObjectTagVisibility(activeImage, id, "text_mask"),
}) })
applyScreenSpaceTagCompensation(text, tagAnchor)
applyScreenSpaceTagCompensation(textMask, tagAnchor)
text.bringToFront() text.bringToFront()
} }
@@ -276,7 +278,7 @@ const useRenderTag = () => {
tag_category: "mask", tag_category: "mask",
}, },
parent: group, parent: group,
visible: visible && configs.includes("外框"), visible: getObjectTagVisibility(activeImage, id, "mask"),
}) })
// 标注对象点id // 标注对象点id
if (path instanceof paper.CompoundPath) { if (path instanceof paper.CompoundPath) {
@@ -301,9 +303,9 @@ const useRenderTag = () => {
tag_anchor: pointTextPosition, tag_anchor: pointTextPosition,
}, },
parent: group, parent: group,
visible: visible && configs.includes("点ID"), visible: getObjectTagVisibility(activeImage, id, "point_text"),
}) })
applyPointTagCompensation(pointText, pointTextPosition) applyScreenSpaceTagCompensation(pointText, pointTextPosition)
index++ index++
}) })
}) })
@@ -327,9 +329,9 @@ const useRenderTag = () => {
tag_anchor: pointTextPosition, tag_anchor: pointTextPosition,
}, },
parent: group, parent: group,
visible: visible && configs.includes("点ID"), visible: getObjectTagVisibility(activeImage, id, "point_text"),
}) })
applyPointTagCompensation(pointText, pointTextPosition) applyScreenSpaceTagCompensation(pointText, pointTextPosition)
}) })
} }
}) })

View File

@@ -5,7 +5,7 @@ import { usePaperStore } from "../usePaperStore"
import { usePaperSupportStore } from "../usePaperSupportStore" import { usePaperSupportStore } from "../usePaperSupportStore"
import { useTopToolsStore } from "../useTopToolsStore" import { useTopToolsStore } from "../useTopToolsStore"
const getTagVisibility = (tagCategory?: string) => { const getTagVisibilityByConfig = (tagCategory?: string) => {
const { showTags, showTagsConfigs } = useTopToolsStore.getState() const { showTags, showTagsConfigs } = useTopToolsStore.getState()
if (!showTags) return false if (!showTags) return false
if (tagCategory === "mask") return showTagsConfigs.includes("外框") if (tagCategory === "mask") return showTagsConfigs.includes("外框")
@@ -13,6 +13,16 @@ const getTagVisibility = (tagCategory?: string) => {
return true return true
} }
export const getObjectTagVisibility = (
imageId: string,
objectId: number,
tagCategory?: string
) => {
const { pathStatus } = useObjectStore.getState()
if (pathStatus[imageId + objectId]?.HIDE) return false
return getTagVisibilityByConfig(tagCategory)
}
const setSelectedItemFalse = (id: number) => { const setSelectedItemFalse = (id: number) => {
if (!id) return if (!id) return
const sameIdItems = usePaperStore.getState().getItemsById(id) const sameIdItems = usePaperStore.getState().getItemsById(id)
@@ -50,7 +60,11 @@ const clearHiddenObjectSelectionState = (imageId: string, objectId: number) => {
} }
} }
const syncObjectItemsVisibility = (objectId: number, hide: boolean) => { const syncObjectItemsVisibility = (
imageId: string,
objectId: number,
hide: boolean
) => {
const sameIdItems = usePaperStore.getState().getItemsById(objectId) const sameIdItems = usePaperStore.getState().getItemsById(objectId)
if (!sameIdItems?.length) return if (!sameIdItems?.length) return
sameIdItems.forEach((item) => { sameIdItems.forEach((item) => {
@@ -59,7 +73,11 @@ const syncObjectItemsVisibility = (objectId: number, hide: boolean) => {
return return
} }
if (item.data?.type === "tag") { if (item.data?.type === "tag") {
item.visible = getTagVisibility(item.data?.tag_category) item.visible = getObjectTagVisibility(
imageId,
objectId,
item.data?.tag_category
)
return return
} }
item.visible = true item.visible = true
@@ -77,7 +95,7 @@ export const toggleObjectHideByShortcut = (
if (hide) { if (hide) {
updatePathStatus(imageId + objectId, "HIDE", hide) updatePathStatus(imageId + objectId, "HIDE", hide)
syncObjectItemsVisibility(objectId, hide) syncObjectItemsVisibility(imageId, objectId, hide)
setSelectedItemFalse(objectId) setSelectedItemFalse(objectId)
clearHiddenObjectSelectionState(imageId, objectId) clearHiddenObjectSelectionState(imageId, objectId)
@@ -96,5 +114,5 @@ export const toggleObjectHideByShortcut = (
updateOperationStatus(imageId + operationId, "HIDE", hide) updateOperationStatus(imageId + operationId, "HIDE", hide)
updatePathStatus(imageId + objectId, "HIDE", hide) updatePathStatus(imageId + objectId, "HIDE", hide)
syncObjectItemsVisibility(objectId, hide) syncObjectItemsVisibility(imageId, objectId, hide)
} }