feat(tool): hide all object

This commit is contained in:
zhangheng
2026-04-10 11:14:06 +08:00
parent bd32d5318d
commit 6aff25a38f

View File

@@ -355,13 +355,10 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
if (operationMap) {
const objList = operationMap.get(operationId)
if (objList && objList.length) {
let hideFlag = true
objList.forEach(([id]) => {
const objStatus =
useObjectStore.getState().pathStatus[`${imgId}${id}`]
if (objStatus && !objStatus["HIDE"]) {
hideFlag = false
}
const hideFlag = objList.every(([id]) => {
return useObjectStore.getState().pathStatus[`${imgId}${id}`]?.["HIDE"]
? true
: false
})
updateOperationStatus(imgId + operationId, "HIDE", hideFlag)
}
@@ -401,6 +398,39 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
[activeImage]
)
const activeImageOperations = labelState.get(activeImage) || []
const isObjectHidden = (imageId: string, objectId: number) => {
return pathStatus[imageId + objectId]?.["HIDE"] ?? false
}
const isOperationHidden = (imageId: string, operationId: string) => {
const operationChildren = storeLabel.get(imageId)?.get(operationId) || []
return (
operationChildren.length > 0 &&
operationChildren.every(([objectId]) => isObjectHidden(imageId, objectId))
)
}
const totalObjectCount = activeImageOperations.reduce(
(count, operationId) => {
return (
count + (storeLabel.get(activeImage)?.get(operationId)?.length || 0)
)
},
0
)
const areAllObjectsHidden =
totalObjectCount > 0 &&
activeImageOperations.every((operationId) => {
const operationChildren =
storeLabel.get(activeImage)?.get(operationId) || []
return operationChildren.every(([objectId]) =>
isObjectHidden(activeImage, objectId)
)
})
// const renderObjectName = useCallback((name: string, input: string) => {
// const index = name?.indexOf(input);
// const beforeStr = name?.substring(0, index);
@@ -509,9 +539,22 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
<Box h="100%" w="100%">
<Flex direction="column" h="100%" gap="xs">
<Flex justify="space-between" align="center" px="md" py="xs">
<Title order={4} size="h4">
</Title>
<Group gap="xs">
<ActionIcon
variant="subtle"
aria-label={areAllObjectsHidden ? "显示全部对象" : "隐藏全部对象"}
onClick={() => {
if (!totalObjectCount) return
activeImageOperations.forEach((operationId) => {
operationHide(operationId, !areAllObjectsHidden)
})
}}>
{areAllObjectsHidden ? <EyeOff size={16} /> : <Eye size={16} />}
</ActionIcon>
<Title order={4} size="h4">
</Title>
</Group>
<Group gap="xs">
<TextInput
w={96}
@@ -566,441 +609,424 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
}}>
<ScrollArea style={{ flex: 1 }} px="md">
<Stack gap="xs" pb="md">
{labelState.get(activeImage) &&
labelState.get(activeImage)?.map((operationId) => {
const operationSchema = getOperationSchema(operationId)
const isCollapsed =
operationStatus[activeImage + operationId]?.["COLLAPSE"]
const isHide =
operationStatus[activeImage + operationId]?.HIDE
const hasSubAttrError = getOperationSubAttrStatus(operationId)
const operationLabel = operationSchema?.label_class
const operationChildren =
storeLabel.get(activeImage)?.get(operationId) || []
const objectCount = operationChildren.length
const visibleChildren = operationChildren.filter((child) => {
const { comment } = child[2]
let visibleFlag = true
if (comment && comment.length) {
const latest_comment =
(taskDetail && taskDetail.label_status === 2) ||
comment.length === 1
? comment[comment.length - 1]
: comment[comment.length - 2]
let type = latest_comment.comment_type
if (type === 2) {
const { last_modified_timestamp } = child[2]
if (
last_modified_timestamp &&
last_modified_timestamp > latest_comment.date
)
type = 3
}
visibleFlag = checkBoxsChecked[3 - type]
{activeImageOperations.map((operationId) => {
const operationSchema = getOperationSchema(operationId)
const isCollapsed =
operationStatus[activeImage + operationId]?.["COLLAPSE"]
const isHide = isOperationHidden(activeImage, operationId)
const hasSubAttrError = getOperationSubAttrStatus(operationId)
const operationLabel = operationSchema?.label_class
const operationChildren =
storeLabel.get(activeImage)?.get(operationId) || []
const objectCount = operationChildren.length
const visibleChildren = operationChildren.filter((child) => {
const { comment } = child[2]
let visibleFlag = true
if (comment && comment.length) {
const latest_comment =
(taskDetail && taskDetail.label_status === 2) ||
comment.length === 1
? comment[comment.length - 1]
: comment[comment.length - 2]
let type = latest_comment.comment_type
if (type === 2) {
const { last_modified_timestamp } = child[2]
if (
last_modified_timestamp &&
last_modified_timestamp > latest_comment.date
)
type = 3
}
if (checkBoxsChecked.every((value) => !value))
visibleFlag = true
return visibleFlag
})
return (
<Box key={operationId} w="100%">
<Paper withBorder p="xs" radius="md" shadow="sm">
<Flex align="center" gap="xs" style={{ minWidth: 0 }}>
<Flex
align="center"
gap="xs"
style={{ flex: 1, minWidth: 0 }}>
<ActionIcon
variant="subtle"
size="sm"
onClick={() =>
operationHide(operationId, !isHide)
}>
{!isHide ? (
<Eye size={16} />
) : (
<EyeOff size={16} />
)}
</ActionIcon>
<Group
gap={8}
wrap="nowrap"
style={{ flex: 1, minWidth: 0 }}>
<Group
gap={6}
wrap="nowrap"
px="xs"
py={4}
style={{
flexShrink: 0,
borderRadius: 999,
border: `1px solid ${
hasSubAttrError
? "var(--mantine-color-red-2)"
: "var(--mantine-color-gray-2)"
}`,
background: hasSubAttrError
? "var(--mantine-color-red-0)"
: "var(--mantine-color-gray-0)",
}}>
{renderOperationIcon(operationSchema)}
<Text
size="xs"
fw={700}
c={hasSubAttrError ? "red" : "dimmed"}
style={{
whiteSpace: "nowrap",
lineHeight: 1,
}}>
{renderShortcutKey(operationId)}
</Text>
</Group>
<Box style={{ flex: 1, minWidth: 0 }}>
<Text
size="sm"
fw={500}
c={hasSubAttrError ? "red" : undefined}
title={operationLabel || undefined}
style={{
minWidth: 0,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}>
{operationLabel}
</Text>
</Box>
</Group>
</Flex>
visibleFlag = checkBoxsChecked[3 - type]
}
if (checkBoxsChecked.every((value) => !value))
visibleFlag = true
return visibleFlag
})
return (
<Box key={operationId} w="100%">
<Paper withBorder p="xs" radius="md" shadow="sm">
<Flex align="center" gap="xs" style={{ minWidth: 0 }}>
<Flex
align="center"
gap="xs"
style={{ flex: 1, minWidth: 0 }}>
<ActionIcon
variant="subtle"
size="sm"
onClick={() => operationHide(operationId, !isHide)}>
{!isHide ? <Eye size={16} /> : <EyeOff size={16} />}
</ActionIcon>
<Group
gap={6}
gap={8}
wrap="nowrap"
style={{ flexShrink: 0, marginLeft: "auto" }}>
<TextInput
w={88}
size="xs"
placeholder="搜索子属性"
value={searchSubAttrs[operationId] || ""}
onChange={(e) => {
setSearchSubAttrs((searchValues) => ({
...searchValues,
[operationId]: e.target.value,
}))
}}
onKeyDown={(e) =>
handleSelectedBySearchSubAttr(e, operationId)
}
/>
<Badge
size="sm"
variant="light"
radius="xl"
color={hasSubAttrError ? "red" : "gray"}>
{objectCount}
</Badge>
<ActionIcon
variant="subtle"
size="sm"
onClick={() => {
updateOperationStatus(
activeImage + operationId,
"COLLAPSE",
!isCollapsed
)
style={{ flex: 1, minWidth: 0 }}>
<Group
gap={6}
wrap="nowrap"
px="xs"
py={4}
style={{
flexShrink: 0,
borderRadius: 999,
border: `1px solid ${
hasSubAttrError
? "var(--mantine-color-red-2)"
: "var(--mantine-color-gray-2)"
}`,
background: hasSubAttrError
? "var(--mantine-color-red-0)"
: "var(--mantine-color-gray-0)",
}}>
{!isCollapsed ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</ActionIcon>
{renderOperationIcon(operationSchema)}
<Text
size="xs"
fw={700}
c={hasSubAttrError ? "red" : "dimmed"}
style={{
whiteSpace: "nowrap",
lineHeight: 1,
}}>
{renderShortcutKey(operationId)}
</Text>
</Group>
<Box style={{ flex: 1, minWidth: 0 }}>
<Text
size="sm"
fw={500}
c={hasSubAttrError ? "red" : undefined}
title={operationLabel || undefined}
style={{
minWidth: 0,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}>
{operationLabel}
</Text>
</Box>
</Group>
</Flex>
</Paper>
<Collapse in={!isCollapsed}>
<Stack gap="xs" mt="xs" ml="xs">
{visibleChildren.map((child, childIndex) => {
const { comment } = child[2]
const isSelected = selectedPath[
activeImage
]?.includes(child[0])
const isChildHide =
pathStatus[activeImage + child[0]]?.["HIDE"]
const hasChildError = getSubAttrStatus(
getOperationSchema(operationId),
child[2]?.sub_attributes
)
const isLastVisibleChild =
childIndex === visibleChildren.length - 1
let textColor = undefined
let connectorColor = "var(--mantine-color-gray-4)"
if (hasChildError) {
textColor = "red"
connectorColor = "var(--mantine-color-red-3)"
} else if (isSelected) {
textColor = "white"
connectorColor =
"var(--mantine-primary-color-filled)"
<Group
gap={6}
wrap="nowrap"
style={{ flexShrink: 0, marginLeft: "auto" }}>
<TextInput
w={88}
size="xs"
placeholder="搜索子属性"
value={searchSubAttrs[operationId] || ""}
onChange={(e) => {
setSearchSubAttrs((searchValues) => ({
...searchValues,
[operationId]: e.target.value,
}))
}}
onKeyDown={(e) =>
handleSelectedBySearchSubAttr(e, operationId)
}
/>
<Badge
size="sm"
variant="light"
radius="xl"
color={hasSubAttrError ? "red" : "gray"}>
{objectCount}
</Badge>
<ActionIcon
variant="subtle"
size="sm"
onClick={() => {
updateOperationStatus(
activeImage + operationId,
"COLLAPSE",
!isCollapsed
)
}}>
{!isCollapsed ? (
<ChevronUp size={16} />
) : (
<ChevronDown size={16} />
)}
</ActionIcon>
</Group>
</Flex>
</Paper>
<Collapse in={!isCollapsed}>
<Stack gap="xs" mt="xs" ml="xs">
{visibleChildren.map((child, childIndex) => {
const { comment } = child[2]
const isSelected = selectedPath[
activeImage
]?.includes(child[0])
const isChildHide =
pathStatus[activeImage + child[0]]?.["HIDE"]
const hasChildError = getSubAttrStatus(
getOperationSchema(operationId),
child[2]?.sub_attributes
)
const isLastVisibleChild =
childIndex === visibleChildren.length - 1
return (
let textColor = undefined
let connectorColor = "var(--mantine-color-gray-4)"
if (hasChildError) {
textColor = "red"
connectorColor = "var(--mantine-color-red-3)"
} else if (isSelected) {
textColor = "white"
connectorColor =
"var(--mantine-primary-color-filled)"
}
return (
<Box
key={child[0]}
pl="lg"
style={{ position: "relative" }}>
<Box
key={child[0]}
pl="lg"
style={{ position: "relative" }}>
style={{
position: "absolute",
left: 7,
top: 0,
bottom: isLastVisibleChild ? "75%" : 0,
width: 1.5,
borderRadius: 999,
background: connectorColor,
opacity: isSelected ? 0.9 : 0.35,
}}
/>
{isLastVisibleChild ? (
<Box
style={{
position: "absolute",
left: 7,
top: 0,
bottom: isLastVisibleChild ? "75%" : 0,
width: 1.5,
borderRadius: 999,
background: connectorColor,
opacity: isSelected ? 0.9 : 0.35,
top: "calc(50% - 10px)",
width: 14,
height: 10,
borderLeft: `1.5px solid ${connectorColor}`,
borderBottom: `1.5px solid ${connectorColor}`,
borderBottomLeftRadius: 10,
opacity: isSelected ? 0.9 : 0.55,
}}
/>
{isLastVisibleChild ? (
) : (
<>
<Box
style={{
position: "absolute",
left: 7,
top: "calc(50% - 10px)",
top: "50%",
width: 14,
height: 10,
borderLeft: `1.5px solid ${connectorColor}`,
borderBottom: `1.5px solid ${connectorColor}`,
borderBottomLeftRadius: 10,
opacity: isSelected ? 0.9 : 0.55,
borderTop: `1.5px solid ${connectorColor}`,
opacity: isSelected ? 0.9 : 0.45,
}}
/>
) : (
<>
<Box
style={{
position: "absolute",
left: 7,
top: "50%",
width: 14,
borderTop: `1.5px solid ${connectorColor}`,
opacity: isSelected ? 0.9 : 0.45,
}}
/>
<Box
style={{
position: "absolute",
left: 4,
top: "calc(50% - 3px)",
width: 6,
height: 6,
borderRadius: 999,
background: connectorColor,
boxShadow:
"0 0 0 2px var(--mantine-color-body)",
opacity: isSelected ? 1 : 0.85,
}}
/>
</>
)}
<Paper
withBorder
p="xs"
radius="md"
shadow="sm"
bg={
isSelected
? "var(--mantine-primary-color-filled)"
: "var(--mantine-color-gray-0)"
}
c={isSelected ? "white" : undefined}
onClick={() =>
handleSelect(child[0], operationId)
}
style={{ cursor: "pointer" }}>
<Flex justify="space-between" align="center">
<Group gap="xs">
<ActionIcon
variant="subtle"
size="sm"
c={isSelected ? "white" : undefined}
onClick={(e) => {
e.stopPropagation()
objectHide(
operationId,
child[0],
!isChildHide
)
}}>
{!isChildHide ? (
<Eye size={16} />
) : (
<EyeOff size={16} />
)}
</ActionIcon>
<Text size="sm" c={textColor}>
{child[0]?.toString()}
</Text>
<Tooltip
label={
<Stack gap={0}>
<Box
style={{
position: "absolute",
left: 4,
top: "calc(50% - 3px)",
width: 6,
height: 6,
borderRadius: 999,
background: connectorColor,
boxShadow:
"0 0 0 2px var(--mantine-color-body)",
opacity: isSelected ? 1 : 0.85,
}}
/>
</>
)}
<Paper
withBorder
p="xs"
radius="md"
shadow="sm"
bg={
isSelected
? "var(--mantine-primary-color-filled)"
: "var(--mantine-color-gray-0)"
}
c={isSelected ? "white" : undefined}
onClick={() =>
handleSelect(child[0], operationId)
}
style={{ cursor: "pointer" }}>
<Flex justify="space-between" align="center">
<Group gap="xs">
<ActionIcon
variant="subtle"
size="sm"
c={isSelected ? "white" : undefined}
onClick={(e) => {
e.stopPropagation()
objectHide(
operationId,
child[0],
!isChildHide
)
}}>
{!isChildHide ? (
<Eye size={16} />
) : (
<EyeOff size={16} />
)}
</ActionIcon>
<Text size="sm" c={textColor}>
{child[0]?.toString()}
</Text>
<Tooltip
label={
<Stack gap={0}>
<Group gap={4}>
<Text size="xs"></Text>
<Text size="xs">
{
userOpts.find(
(item) =>
item.value === child[2]?.uid
)?.label
}
</Text>
<Text size="xs">
{dayjs(
(child[2]?.create_timestamp ||
0) * 1000
).format("YYYY-MM-DD HH:mm:ss")}
</Text>
</Group>
{child[2]?.first_modified_uid && (
<Group gap={4}>
<Text size="xs"></Text>
<Text size="xs"></Text>
<Text size="xs">
{
userOpts.find(
(item) =>
item.value ===
child[2]?.uid
child[2]
?.first_modified_uid
)?.label
}
</Text>
<Text size="xs">
{dayjs(
(child[2]?.create_timestamp ||
(child[2]
?.first_modified_timestamp ||
0) * 1000
).format("YYYY-MM-DD HH:mm:ss")}
</Text>
</Group>
{child[2]?.first_modified_uid && (
<Group gap={4}>
<Text size="xs">
</Text>
<Text size="xs">
{
userOpts.find(
(item) =>
item.value ===
child[2]
?.first_modified_uid
)?.label
}
</Text>
<Text size="xs">
{dayjs(
(child[2]
?.first_modified_timestamp ||
0) * 1000
).format(
"YYYY-MM-DD HH:mm:ss"
)}
</Text>
</Group>
)}
{child[2]?.last_modified_uid && (
<Group gap={4}>
<Text size="xs">
</Text>
<Text size="xs">
{
userOpts.find(
(item) =>
item.value ===
child[2]
?.last_modified_uid
)?.label
}
</Text>
<Text size="xs">
{dayjs(
(child[2]
?.last_modified_timestamp ||
0) * 1000
).format(
"YYYY-MM-DD HH:mm:ss"
)}
</Text>
</Group>
)}
</Stack>
}
multiline
bg="white"
c="black"
withArrow>
<Info
size={16}
style={{ cursor: "pointer" }}
color={isSelected ? "white" : "black"}
/>
</Tooltip>
)}
{child[2]?.last_modified_uid && (
<Group gap={4}>
<Text size="xs"></Text>
<Text size="xs">
{
userOpts.find(
(item) =>
item.value ===
child[2]
?.last_modified_uid
)?.label
}
</Text>
<Text size="xs">
{dayjs(
(child[2]
?.last_modified_timestamp ||
0) * 1000
).format("YYYY-MM-DD HH:mm:ss")}
</Text>
</Group>
)}
</Stack>
}
multiline
bg="white"
c="black"
withArrow>
<Info
size={16}
style={{ cursor: "pointer" }}
color={isSelected ? "white" : "black"}
/>
</Tooltip>
</Group>
{comment ? (
<Group gap={4}>
{comment.map((c: any) => {
const count =
c.check_box_comments.length +
c.text_comments.length
const { last_modified_timestamp } =
child[2]
let flag = false
if (
count > 0 &&
last_modified_timestamp &&
last_modified_timestamp > c.date
)
flag = true
let badgeColor = "gray"
if (c.comment_type === 1)
badgeColor = "green"
if (c.comment_type === 2)
badgeColor = "red"
if (flag) badgeColor = "yellow"
return (
<Popover
key={`${child[0]}${c.turn}${c.times}`}
position="left"
withArrow>
<Popover.Target>
<Badge
size="xs"
circle
color={badgeColor}
style={{
cursor: "pointer",
}}>
{count}
</Badge>
</Popover.Target>
<Popover.Dropdown>
<Text size="xs" c={badgeColor}>
{`P${child[2].image_id}${
child[2].uid
}${c.check_box_comments.join(
","
)},${c.text_comments.join(
","
)} ${
c.date
? dayjs(
c.date * 1000
).format(
"YYYY-MM-DD HH:mm:ss"
)
: ""
}`}
</Text>
</Popover.Dropdown>
</Popover>
)
})}
</Group>
{comment ? (
<Group gap={4}>
{comment.map((c: any) => {
const count =
c.check_box_comments.length +
c.text_comments.length
const { last_modified_timestamp } =
child[2]
let flag = false
if (
count > 0 &&
last_modified_timestamp &&
last_modified_timestamp > c.date
)
flag = true
let badgeColor = "gray"
if (c.comment_type === 1)
badgeColor = "green"
if (c.comment_type === 2)
badgeColor = "red"
if (flag) badgeColor = "yellow"
return (
<Popover
key={`${child[0]}${c.turn}${c.times}`}
position="left"
withArrow>
<Popover.Target>
<Badge
size="xs"
circle
color={badgeColor}
style={{
cursor: "pointer",
}}>
{count}
</Badge>
</Popover.Target>
<Popover.Dropdown>
<Text size="xs" c={badgeColor}>
{`P${child[2].image_id}${
child[2].uid
}${c.check_box_comments.join(
","
)},${c.text_comments.join(
","
)} ${
c.date
? dayjs(
c.date * 1000
).format(
"YYYY-MM-DD HH:mm:ss"
)
: ""
}`}
</Text>
</Popover.Dropdown>
</Popover>
)
})}
</Group>
) : null}
</Flex>
</Paper>
</Box>
)
})}
</Stack>
</Collapse>
</Box>
)
})}
) : null}
</Flex>
</Paper>
</Box>
)
})}
</Stack>
</Collapse>
</Box>
)
})}
</Stack>
</ScrollArea>
</Collapse>