Merge branch 'zh' into 'main'
Zh See merge request prism/lable/labelimage!10
This commit is contained in:
@@ -17,6 +17,7 @@ fetch 参数:
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useUserStore } from "@/app/store/user"
|
import { useUserStore } from "@/app/store/user"
|
||||||
|
import { usePermissionStore } from "@/components/label/store/auth"
|
||||||
import { deleteCookie, refreshKey } from "@/components/login/api"
|
import { deleteCookie, refreshKey } from "@/components/login/api"
|
||||||
import { isNumber } from "@/libs/util"
|
import { isNumber } from "@/libs/util"
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ interface OptionsProps {
|
|||||||
|
|
||||||
async function httpfetch<T>(options: OptionsProps): Promise<T> {
|
async function httpfetch<T>(options: OptionsProps): Promise<T> {
|
||||||
const { removeUserInfo, refreshToken } = useUserStore.getState()
|
const { removeUserInfo, refreshToken } = useUserStore.getState()
|
||||||
|
const { logout } = usePermissionStore.getState()
|
||||||
|
|
||||||
const access_token = useUserStore.getState().access_token
|
const access_token = useUserStore.getState().access_token
|
||||||
const refresh_token = useUserStore.getState().refresh_token
|
const refresh_token = useUserStore.getState().refresh_token
|
||||||
@@ -197,6 +199,7 @@ async function httpfetch<T>(options: OptionsProps): Promise<T> {
|
|||||||
} finally {
|
} finally {
|
||||||
// 清空身份信息并跳转至登录页面
|
// 清空身份信息并跳转至登录页面
|
||||||
removeUserInfo()
|
removeUserInfo()
|
||||||
|
logout()
|
||||||
window.location.href = "/login"
|
window.location.href = "/login"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,6 +244,7 @@ async function httpfetch<T>(options: OptionsProps): Promise<T> {
|
|||||||
} finally {
|
} finally {
|
||||||
// 清空身份信息并跳转至登录页面
|
// 清空身份信息并跳转至登录页面
|
||||||
removeUserInfo()
|
removeUserInfo()
|
||||||
|
logout()
|
||||||
window.location.href = "/login"
|
window.location.href = "/login"
|
||||||
}
|
}
|
||||||
} else if (status >= 510) {
|
} else if (status >= 510) {
|
||||||
|
|||||||
@@ -181,7 +181,8 @@ export default function TaskTableContainer(props: {
|
|||||||
}) {
|
}) {
|
||||||
const { info, userEnums } = props
|
const { info, userEnums } = props
|
||||||
|
|
||||||
const { user_id } = usePermissionStore()
|
const { user_id, detailInfo } = usePermissionStore()
|
||||||
|
const hasRolePermissions = Boolean(detailInfo?.roles?.length)
|
||||||
|
|
||||||
const urlParams = useSearchParams()
|
const urlParams = useSearchParams()
|
||||||
const id = urlParams.get("id")
|
const id = urlParams.get("id")
|
||||||
@@ -775,6 +776,7 @@ export default function TaskTableContainer(props: {
|
|||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
render: (record) => (
|
render: (record) => (
|
||||||
<Flex gap={6} justify="space-around" wrap="nowrap">
|
<Flex gap={6} justify="space-around" wrap="nowrap">
|
||||||
|
{hasRolePermissions ? (
|
||||||
<Button
|
<Button
|
||||||
px={0}
|
px={0}
|
||||||
variant="transparent"
|
variant="transparent"
|
||||||
@@ -791,6 +793,7 @@ export default function TaskTableContainer(props: {
|
|||||||
}}>
|
}}>
|
||||||
调度
|
调度
|
||||||
</Button>
|
</Button>
|
||||||
|
) : null}
|
||||||
<Button
|
<Button
|
||||||
px={0}
|
px={0}
|
||||||
variant="transparent"
|
variant="transparent"
|
||||||
@@ -802,7 +805,7 @@ export default function TaskTableContainer(props: {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
return cols
|
return cols
|
||||||
}, [info, projectId, router, setBackProps, type])
|
}, [hasRolePermissions, info, projectId, router, setBackProps, type])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -984,6 +987,7 @@ export default function TaskTableContainer(props: {
|
|||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
<SettingHeaderActions>
|
<SettingHeaderActions>
|
||||||
|
{hasRolePermissions ? (
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
leftSection={<IconRefresh size={16} />}
|
leftSection={<IconRefresh size={16} />}
|
||||||
@@ -991,24 +995,29 @@ export default function TaskTableContainer(props: {
|
|||||||
loading={loading}>
|
loading={loading}>
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
|
) : null}
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
disabled={!selectedRecords.length}
|
disabled={!selectedRecords.length}
|
||||||
onClick={handleExport}>
|
onClick={handleExport}>
|
||||||
下载选中项
|
下载选中项
|
||||||
</Button>
|
</Button>
|
||||||
|
{hasRolePermissions ? (
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
disabled={!selectedRecords.length}
|
disabled={!selectedRecords.length}
|
||||||
onClick={clickDispatchTask}>
|
onClick={clickDispatchTask}>
|
||||||
调度选中项
|
调度选中项
|
||||||
</Button>
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{hasRolePermissions ? (
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
disabled={!selectedRecords.length}
|
disabled={!selectedRecords.length}
|
||||||
onClick={clickReleaseTask}>
|
onClick={clickReleaseTask}>
|
||||||
释放选中项
|
释放选中项
|
||||||
</Button>
|
</Button>
|
||||||
|
) : null}
|
||||||
</SettingHeaderActions>
|
</SettingHeaderActions>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1512,27 +1512,37 @@ const LabelPage = ({
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleToggleHideSelectedObject = useCallback(() => {
|
const handleToggleHideAllObjects = useCallback(() => {
|
||||||
const selectedContext = resolveSelectedObjectContext()
|
const activeImageLabel = labelData.get(activeImage)
|
||||||
if (!selectedContext) {
|
if (!activeImageLabel?.size) return
|
||||||
notifications.show({
|
|
||||||
color: "yellow",
|
|
||||||
message: "请先选中单个对象",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentHideStatus =
|
const activeImageOperations = Array.from(activeImageLabel.keys())
|
||||||
useObjectStore.getState().pathStatus[
|
const totalObjectCount = activeImageOperations.reduce(
|
||||||
selectedContext.imageId + selectedContext.objectId
|
(count, operationId) => {
|
||||||
]?.HIDE || false
|
return count + (activeImageLabel.get(operationId)?.length || 0)
|
||||||
toggleObjectHideByShortcut(
|
},
|
||||||
selectedContext.imageId,
|
0
|
||||||
selectedContext.operationId,
|
|
||||||
selectedContext.objectId,
|
|
||||||
!currentHideStatus
|
|
||||||
)
|
)
|
||||||
}, [resolveSelectedObjectContext])
|
if (!totalObjectCount) return
|
||||||
|
|
||||||
|
const pathStatus = useObjectStore.getState().pathStatus
|
||||||
|
const areAllObjectsHidden = activeImageOperations.every((operationId) => {
|
||||||
|
const operationChildren = activeImageLabel.get(operationId) || []
|
||||||
|
return operationChildren.every(
|
||||||
|
([objectId]) => pathStatus[activeImage + objectId]?.HIDE ?? false
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const nextHide = !areAllObjectsHidden
|
||||||
|
const { updateOperationStatus } = useObjectStore.getState()
|
||||||
|
|
||||||
|
activeImageOperations.forEach((operationId) => {
|
||||||
|
updateOperationStatus(activeImage + operationId, "HIDE", nextHide)
|
||||||
|
activeImageLabel.get(operationId)?.forEach(([objectId]) => {
|
||||||
|
toggleObjectHideByShortcut(activeImage, operationId, objectId, nextHide)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, [activeImage, labelData])
|
||||||
|
|
||||||
const rerenderActiveImageByLabel = useCallback(
|
const rerenderActiveImageByLabel = useCallback(
|
||||||
(
|
(
|
||||||
@@ -1617,6 +1627,8 @@ const LabelPage = ({
|
|||||||
objectId: selectedContext.objectId,
|
objectId: selectedContext.objectId,
|
||||||
})
|
})
|
||||||
usePaperStore.getState().setPaperMode("polygon")
|
usePaperStore.getState().setPaperMode("polygon")
|
||||||
|
const started = usePaperStore.getState().beginContinuePolygonDraft()
|
||||||
|
if (!started) return
|
||||||
notifications.show({
|
notifications.show({
|
||||||
color: "green",
|
color: "green",
|
||||||
message: "已进入继续标注模式",
|
message: "已进入继续标注模式",
|
||||||
@@ -2123,7 +2135,7 @@ const LabelPage = ({
|
|||||||
|
|
||||||
if (!e.ctrlKey && !e.metaKey && !e.altKey && lowerKey === "h") {
|
if (!e.ctrlKey && !e.metaKey && !e.altKey && lowerKey === "h") {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
handleToggleHideSelectedObject()
|
handleToggleHideAllObjects()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2424,7 +2436,7 @@ const LabelPage = ({
|
|||||||
enterSelectedObjectDrawMode,
|
enterSelectedObjectDrawMode,
|
||||||
focusInput,
|
focusInput,
|
||||||
getOperationSchema,
|
getOperationSchema,
|
||||||
handleToggleHideSelectedObject,
|
handleToggleHideAllObjects,
|
||||||
handleMergeSelectedObjects,
|
handleMergeSelectedObjects,
|
||||||
handleContinueSelectedPolygon,
|
handleContinueSelectedPolygon,
|
||||||
handleShortcut,
|
handleShortcut,
|
||||||
|
|||||||
@@ -1291,11 +1291,6 @@ const PaperContainer = (
|
|||||||
setup,
|
setup,
|
||||||
])
|
])
|
||||||
|
|
||||||
const [paperSelectedPath] = useState<{
|
|
||||||
// key = imageId
|
|
||||||
[key: string]: number[]
|
|
||||||
}>(selectedPath)
|
|
||||||
|
|
||||||
const getOperationSchema = useCallback(
|
const getOperationSchema = useCallback(
|
||||||
(operationId: string) => {
|
(operationId: string) => {
|
||||||
return (
|
return (
|
||||||
@@ -1320,6 +1315,8 @@ const PaperContainer = (
|
|||||||
const renderImageId = imageIdOverride ?? activeImage
|
const renderImageId = imageIdOverride ?? activeImage
|
||||||
|
|
||||||
if (operationSchema && imageData) {
|
if (operationSchema && imageData) {
|
||||||
|
const selectedIds =
|
||||||
|
useObjectStore.getState().selectedPath[renderImageId] || []
|
||||||
const toPaperPoint = (segment: any): paper.Point | null => {
|
const toPaperPoint = (segment: any): paper.Point | null => {
|
||||||
if (segment instanceof paper.Point) return segment
|
if (segment instanceof paper.Point) return segment
|
||||||
if (segment instanceof paper.Segment) {
|
if (segment instanceof paper.Segment) {
|
||||||
@@ -1401,9 +1398,7 @@ const PaperContainer = (
|
|||||||
operationId: operationId,
|
operationId: operationId,
|
||||||
fillColor: fillColor,
|
fillColor: fillColor,
|
||||||
blankColor: blankColor,
|
blankColor: blankColor,
|
||||||
selected: paperSelectedPath[renderImageId]?.includes(
|
selected: selectedIds.includes(item[0]),
|
||||||
item[0]
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -1430,9 +1425,7 @@ const PaperContainer = (
|
|||||||
operationId: operationId,
|
operationId: operationId,
|
||||||
fillColor: fillColor,
|
fillColor: fillColor,
|
||||||
blankColor: blankColor,
|
blankColor: blankColor,
|
||||||
selected: paperSelectedPath[renderImageId]?.includes(
|
selected: selectedIds.includes(item[0]),
|
||||||
item[0]
|
|
||||||
),
|
|
||||||
isHollowPolygon: true,
|
isHollowPolygon: true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1456,7 +1449,7 @@ const PaperContainer = (
|
|||||||
operationId: operationId,
|
operationId: operationId,
|
||||||
fillColor: fillColor,
|
fillColor: fillColor,
|
||||||
blankColor: blankColor,
|
blankColor: blankColor,
|
||||||
selected: paperSelectedPath[renderImageId]?.includes(item[0]),
|
selected: selectedIds.includes(item[0]),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1534,9 +1527,7 @@ const PaperContainer = (
|
|||||||
operationId: operationId,
|
operationId: operationId,
|
||||||
fillColor: fillColor,
|
fillColor: fillColor,
|
||||||
blankColor: blankColor,
|
blankColor: blankColor,
|
||||||
selected: paperSelectedPath[renderImageId]?.includes(
|
selected: selectedIds.includes(item[0]),
|
||||||
item[0]
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -1567,9 +1558,7 @@ const PaperContainer = (
|
|||||||
operationId: operationId,
|
operationId: operationId,
|
||||||
fillColor: fillColor,
|
fillColor: fillColor,
|
||||||
blankColor: blankColor,
|
blankColor: blankColor,
|
||||||
selected: paperSelectedPath[renderImageId]?.includes(
|
selected: selectedIds.includes(item[0]),
|
||||||
item[0]
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -1592,7 +1581,7 @@ const PaperContainer = (
|
|||||||
operationId: operationId,
|
operationId: operationId,
|
||||||
fillColor: fillColor,
|
fillColor: fillColor,
|
||||||
blankColor: blankColor,
|
blankColor: blankColor,
|
||||||
selected: paperSelectedPath[renderImageId]?.includes(item[0]),
|
selected: selectedIds.includes(item[0]),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -1632,9 +1621,7 @@ const PaperContainer = (
|
|||||||
operationId: operationId,
|
operationId: operationId,
|
||||||
fillColor: fillColor,
|
fillColor: fillColor,
|
||||||
blankColor: blankColor,
|
blankColor: blankColor,
|
||||||
selected: paperSelectedPath[renderImageId]?.includes(
|
selected: selectedIds.includes(item[0]),
|
||||||
item[0]
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -1655,7 +1642,6 @@ const PaperContainer = (
|
|||||||
addRectangle,
|
addRectangle,
|
||||||
getOperationSchema,
|
getOperationSchema,
|
||||||
paperPathStatus,
|
paperPathStatus,
|
||||||
paperSelectedPath,
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1765,6 +1751,7 @@ const PaperContainer = (
|
|||||||
detail,
|
detail,
|
||||||
hollow_segmentation,
|
hollow_segmentation,
|
||||||
])
|
])
|
||||||
|
useObjectStore.getState().updateSelectedPath(activeImage, "ADD", id)
|
||||||
console.log(usePaperStore.getState().group)
|
console.log(usePaperStore.getState().group)
|
||||||
// console.log(first)
|
// console.log(first)
|
||||||
// 更新当前页绘制数据
|
// 更新当前页绘制数据
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import { getShowContextMenuPosition, usePaperStore } from "../usePaperStore"
|
|||||||
import { renderGroupPath } from "../useRenderGroupPath"
|
import { renderGroupPath } from "../useRenderGroupPath"
|
||||||
import { useRightToolsStore } from "../useRightToolsStore"
|
import { useRightToolsStore } from "../useRightToolsStore"
|
||||||
import { useTopToolsStore } from "../useTopToolsStore"
|
import { useTopToolsStore } from "../useTopToolsStore"
|
||||||
import { labelTypeMap } from "../utils/constants"
|
|
||||||
import { safeClone } from "../utils/clone"
|
import { safeClone } from "../utils/clone"
|
||||||
|
import { labelTypeMap } from "../utils/constants"
|
||||||
import RightGroupEditModal from "./RightGroupEditModal"
|
import RightGroupEditModal from "./RightGroupEditModal"
|
||||||
|
|
||||||
interface RightGroupToolsComponentProps {
|
interface RightGroupToolsComponentProps {
|
||||||
@@ -56,9 +56,25 @@ const RightGroupTools: React.FC<RightGroupToolsComponentProps> = (props) => {
|
|||||||
const { activeImage } = useBottomToolsStore()
|
const { activeImage } = useBottomToolsStore()
|
||||||
const { groupStatus, updateGroupStatus, selectedPath, updateSelectedPath } =
|
const { groupStatus, updateGroupStatus, selectedPath, updateSelectedPath } =
|
||||||
useObjectStore()
|
useObjectStore()
|
||||||
|
const continuePolygonTarget = usePaperStore(
|
||||||
|
(state) => state.continuePolygonTarget
|
||||||
|
)
|
||||||
const { getItemById, getItemsById } = usePaperStore()
|
const { getItemById, getItemsById } = usePaperStore()
|
||||||
const { shift: pressShift } = useKeyEventStore()
|
const { shift: pressShift } = useKeyEventStore()
|
||||||
const [isGroupCollapsed, setIsGroupCollapsed] = useState<boolean>(true)
|
const [isGroupCollapsed, setIsGroupCollapsed] = useState<boolean>(true)
|
||||||
|
const continueObjectId =
|
||||||
|
continuePolygonTarget?.imageId === activeImage
|
||||||
|
? continuePolygonTarget.objectId
|
||||||
|
: null
|
||||||
|
|
||||||
|
const getGroupChildren = useCallback(
|
||||||
|
(groupId: number) => {
|
||||||
|
const groupChildren = pathGroupMap.get(activeImage)?.get(groupId) || []
|
||||||
|
if (continueObjectId === null) return groupChildren
|
||||||
|
return groupChildren.filter((childId) => childId !== continueObjectId)
|
||||||
|
},
|
||||||
|
[activeImage, continueObjectId, pathGroupMap]
|
||||||
|
)
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
const handleSelect = useCallback(
|
||||||
(id: number) => {
|
(id: number) => {
|
||||||
@@ -322,8 +338,10 @@ const RightGroupTools: React.FC<RightGroupToolsComponentProps> = (props) => {
|
|||||||
<ScrollArea style={{ flex: 1 }} px="md">
|
<ScrollArea style={{ flex: 1 }} px="md">
|
||||||
<Stack gap="xs" pb="md">
|
<Stack gap="xs" pb="md">
|
||||||
{pathGroupMap.get(activeImage) &&
|
{pathGroupMap.get(activeImage) &&
|
||||||
Array.from(pathGroupMap.get(activeImage)!.keys())?.map(
|
Array.from(pathGroupMap.get(activeImage)!.keys())
|
||||||
(groupId) => {
|
.filter((groupId) => getGroupChildren(groupId).length)
|
||||||
|
.map((groupId) => {
|
||||||
|
const groupChildren = getGroupChildren(groupId)
|
||||||
const isOpen =
|
const isOpen =
|
||||||
!groupStatus[activeImage + groupId]?.["COLLAPSE"]
|
!groupStatus[activeImage + groupId]?.["COLLAPSE"]
|
||||||
return (
|
return (
|
||||||
@@ -376,10 +394,7 @@ const RightGroupTools: React.FC<RightGroupToolsComponentProps> = (props) => {
|
|||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<Text size="sm">
|
<Text size="sm">{groupChildren.length}</Text>
|
||||||
{pathGroupMap.get(activeImage)?.get(groupId)
|
|
||||||
?.length || 0}
|
|
||||||
</Text>
|
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -401,10 +416,7 @@ const RightGroupTools: React.FC<RightGroupToolsComponentProps> = (props) => {
|
|||||||
</Paper>
|
</Paper>
|
||||||
<Collapse in={isOpen}>
|
<Collapse in={isOpen}>
|
||||||
<Stack gap="xs" mt="xs" pl="xs">
|
<Stack gap="xs" mt="xs" pl="xs">
|
||||||
{pathGroupMap
|
{groupChildren.map((child) => {
|
||||||
.get(activeImage)
|
|
||||||
?.get(groupId)
|
|
||||||
?.map((child) => {
|
|
||||||
const isSelected =
|
const isSelected =
|
||||||
selectedPath[activeImage]?.includes(child)
|
selectedPath[activeImage]?.includes(child)
|
||||||
return (
|
return (
|
||||||
@@ -444,8 +456,7 @@ const RightGroupTools: React.FC<RightGroupToolsComponentProps> = (props) => {
|
|||||||
</Collapse>
|
</Collapse>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
})}
|
||||||
)}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ import { getShowContextMenuPosition, usePaperStore } from "../usePaperStore"
|
|||||||
import { usePaperSupportStore } from "../usePaperSupportStore"
|
import { usePaperSupportStore } from "../usePaperSupportStore"
|
||||||
import { useTopToolsStore } from "../useTopToolsStore"
|
import { useTopToolsStore } from "../useTopToolsStore"
|
||||||
import { getSubAttrStatus } from "../util"
|
import { getSubAttrStatus } from "../util"
|
||||||
import { toggleObjectHideByShortcut } from "../utils/objectVisibility"
|
|
||||||
import { labelTypeMap } from "../utils/constants"
|
import { labelTypeMap } from "../utils/constants"
|
||||||
|
import { toggleObjectHideByShortcut } from "../utils/objectVisibility"
|
||||||
|
|
||||||
interface RightObjectToolsComponentProps {
|
interface RightObjectToolsComponentProps {
|
||||||
labelState: LabelState
|
labelState: LabelState
|
||||||
@@ -143,7 +143,27 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
|
|
||||||
const { shift: pressShift } = useKeyEventStore()
|
const { shift: pressShift } = useKeyEventStore()
|
||||||
|
|
||||||
|
const continuePolygonTarget = usePaperStore(
|
||||||
|
(state) => state.continuePolygonTarget
|
||||||
|
)
|
||||||
const { getItemById, getItemsById } = usePaperStore()
|
const { getItemById, getItemsById } = usePaperStore()
|
||||||
|
const continueObjectId =
|
||||||
|
continuePolygonTarget?.imageId === activeImage
|
||||||
|
? continuePolygonTarget.objectId
|
||||||
|
: null
|
||||||
|
|
||||||
|
const getOperationChildren = useCallback(
|
||||||
|
(imageId: string, operationId: string) => {
|
||||||
|
const operationChildren = storeLabel.get(imageId)?.get(operationId) || []
|
||||||
|
if (continueObjectId === null || imageId !== activeImage) {
|
||||||
|
return operationChildren
|
||||||
|
}
|
||||||
|
return operationChildren.filter(
|
||||||
|
([objectId]) => objectId !== continueObjectId
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[activeImage, continueObjectId, storeLabel]
|
||||||
|
)
|
||||||
|
|
||||||
// 切换选中对象时,清除所有其他对象的当前选中状态
|
// 切换选中对象时,清除所有其他对象的当前选中状态
|
||||||
const clearSelectedItems = () => {
|
const clearSelectedItems = () => {
|
||||||
@@ -381,14 +401,11 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
(operationId: string, hide: boolean) => {
|
(operationId: string, hide: boolean) => {
|
||||||
updateOperationStatus(activeImage + operationId, "HIDE", hide)
|
updateOperationStatus(activeImage + operationId, "HIDE", hide)
|
||||||
|
|
||||||
storeLabel
|
getOperationChildren(activeImage, operationId).map((child) => {
|
||||||
.get(activeImage)
|
|
||||||
?.get(operationId)
|
|
||||||
?.map((child) => {
|
|
||||||
toggleObjectHideByShortcut(activeImage, operationId, child[0], hide)
|
toggleObjectHideByShortcut(activeImage, operationId, child[0], hide)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[activeImage, storeLabel, updateOperationStatus]
|
[activeImage, getOperationChildren, updateOperationStatus]
|
||||||
)
|
)
|
||||||
|
|
||||||
const objectHide = useCallback(
|
const objectHide = useCallback(
|
||||||
@@ -398,14 +415,16 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
[activeImage]
|
[activeImage]
|
||||||
)
|
)
|
||||||
|
|
||||||
const activeImageOperations = labelState.get(activeImage) || []
|
const activeImageOperations = (labelState.get(activeImage) || []).filter(
|
||||||
|
(operationId) => getOperationChildren(activeImage, operationId).length
|
||||||
|
)
|
||||||
|
|
||||||
const isObjectHidden = (imageId: string, objectId: number) => {
|
const isObjectHidden = (imageId: string, objectId: number) => {
|
||||||
return pathStatus[imageId + objectId]?.["HIDE"] ?? false
|
return pathStatus[imageId + objectId]?.["HIDE"] ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOperationHidden = (imageId: string, operationId: string) => {
|
const isOperationHidden = (imageId: string, operationId: string) => {
|
||||||
const operationChildren = storeLabel.get(imageId)?.get(operationId) || []
|
const operationChildren = getOperationChildren(imageId, operationId)
|
||||||
return (
|
return (
|
||||||
operationChildren.length > 0 &&
|
operationChildren.length > 0 &&
|
||||||
operationChildren.every(([objectId]) => isObjectHidden(imageId, objectId))
|
operationChildren.every(([objectId]) => isObjectHidden(imageId, objectId))
|
||||||
@@ -414,9 +433,7 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
|
|
||||||
const totalObjectCount = activeImageOperations.reduce(
|
const totalObjectCount = activeImageOperations.reduce(
|
||||||
(count, operationId) => {
|
(count, operationId) => {
|
||||||
return (
|
return count + getOperationChildren(activeImage, operationId).length
|
||||||
count + (storeLabel.get(activeImage)?.get(operationId)?.length || 0)
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
@@ -424,8 +441,7 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
const areAllObjectsHidden =
|
const areAllObjectsHidden =
|
||||||
totalObjectCount > 0 &&
|
totalObjectCount > 0 &&
|
||||||
activeImageOperations.every((operationId) => {
|
activeImageOperations.every((operationId) => {
|
||||||
const operationChildren =
|
const operationChildren = getOperationChildren(activeImage, operationId)
|
||||||
storeLabel.get(activeImage)?.get(operationId) || []
|
|
||||||
return operationChildren.every(([objectId]) =>
|
return operationChildren.every(([objectId]) =>
|
||||||
isObjectHidden(activeImage, objectId)
|
isObjectHidden(activeImage, objectId)
|
||||||
)
|
)
|
||||||
@@ -451,6 +467,9 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
.get(activeImage)
|
.get(activeImage)
|
||||||
?.get(operationId)
|
?.get(operationId)
|
||||||
?.some((child) => {
|
?.some((child) => {
|
||||||
|
if (continueObjectId !== null && child[0] === continueObjectId) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return getSubAttrStatus(
|
return getSubAttrStatus(
|
||||||
getOperationSchema(operationId),
|
getOperationSchema(operationId),
|
||||||
child[2]?.sub_attributes
|
child[2]?.sub_attributes
|
||||||
@@ -458,7 +477,7 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
})
|
})
|
||||||
return bool
|
return bool
|
||||||
},
|
},
|
||||||
[activeImage, getOperationSchema, storeLabel]
|
[activeImage, continueObjectId, getOperationSchema, storeLabel]
|
||||||
)
|
)
|
||||||
|
|
||||||
// 搜索标注对象id
|
// 搜索标注对象id
|
||||||
@@ -468,7 +487,8 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
const id = Number(searchId)
|
const id = Number(searchId)
|
||||||
const labelObjMap = storeLabel.get(activeImage)
|
const labelObjMap = storeLabel.get(activeImage)
|
||||||
Array.from(labelObjMap!.entries()).forEach(([operationId, objList]) => {
|
Array.from(labelObjMap!.entries()).forEach(([operationId, objList]) => {
|
||||||
objList.forEach((obj) => {
|
false && objList
|
||||||
|
getOperationChildren(activeImage, operationId).forEach((obj) => {
|
||||||
if (id === obj[0]) {
|
if (id === obj[0]) {
|
||||||
handleSelect(id, operationId)
|
handleSelect(id, operationId)
|
||||||
}
|
}
|
||||||
@@ -483,7 +503,7 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
Array.from(storeLabel.entries()).forEach(([imgId, objectMap]) => {
|
Array.from(storeLabel.entries()).forEach(([imgId, objectMap]) => {
|
||||||
if (objectMap.get(currentOperationId)) {
|
if (objectMap.get(currentOperationId)) {
|
||||||
const list = objectMap.get(currentOperationId)
|
const list = getOperationChildren(imgId, currentOperationId)
|
||||||
list!.forEach((item) => {
|
list!.forEach((item) => {
|
||||||
const { sub_attributes } = item[2]
|
const { sub_attributes } = item[2]
|
||||||
// 如果没有子属性搜索值,则把对象HIDE设为false
|
// 如果没有子属性搜索值,则把对象HIDE设为false
|
||||||
@@ -616,8 +636,10 @@ const RightObjectTools: React.FC<RightObjectToolsComponentProps> = (props) => {
|
|||||||
const isHide = isOperationHidden(activeImage, operationId)
|
const isHide = isOperationHidden(activeImage, operationId)
|
||||||
const hasSubAttrError = getOperationSubAttrStatus(operationId)
|
const hasSubAttrError = getOperationSubAttrStatus(operationId)
|
||||||
const operationLabel = operationSchema?.label_class
|
const operationLabel = operationSchema?.label_class
|
||||||
const operationChildren =
|
const operationChildren = getOperationChildren(
|
||||||
storeLabel.get(activeImage)?.get(operationId) || []
|
activeImage,
|
||||||
|
operationId
|
||||||
|
)
|
||||||
const objectCount = operationChildren.length
|
const objectCount = operationChildren.length
|
||||||
const visibleChildren = operationChildren.filter((child) => {
|
const visibleChildren = operationChildren.filter((child) => {
|
||||||
const { comment } = child[2]
|
const { comment } = child[2]
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { persist, PersistStorage, StorageValue } from "zustand/middleware"
|
|||||||
import { Comment } from "./api/label/typing"
|
import { Comment } from "./api/label/typing"
|
||||||
import { safeClone } from "./utils/clone"
|
import { safeClone } from "./utils/clone"
|
||||||
|
|
||||||
type LabelData = Map<string, Map<string, [number, any[], any, any[]][]>>
|
export type LabelData = Map<string, Map<string, [number, any[], any, any[]][]>>
|
||||||
type LabelPathTuple = [number, any[], any, any[]]
|
export type LabelPathTuple = [number, any[], any, any[]]
|
||||||
type LabelTime = {
|
type LabelTime = {
|
||||||
label: number
|
label: number
|
||||||
review1: number
|
review1: number
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { Comment } from "./api/label/typing"
|
|||||||
import { Project } from "./api/project/typing"
|
import { Project } from "./api/project/typing"
|
||||||
import {
|
import {
|
||||||
initialDetail,
|
initialDetail,
|
||||||
|
type LabelPathTuple,
|
||||||
useKeyEventStore,
|
useKeyEventStore,
|
||||||
useLabelStore,
|
useLabelStore,
|
||||||
useObjectStore,
|
useObjectStore,
|
||||||
@@ -180,6 +181,7 @@ interface PaperState {
|
|||||||
getItemById: (id: number) => paper.Path | null
|
getItemById: (id: number) => paper.Path | null
|
||||||
getItemsById: (id: number) => Array<paper.Path | paper.CompoundPath>
|
getItemsById: (id: number) => Array<paper.Path | paper.CompoundPath>
|
||||||
setContinuePolygonTarget: (target: ContinuePolygonTarget | null) => void
|
setContinuePolygonTarget: (target: ContinuePolygonTarget | null) => void
|
||||||
|
beginContinuePolygonDraft: () => boolean
|
||||||
setLoadingData: (flag: boolean) => void
|
setLoadingData: (flag: boolean) => void
|
||||||
resizeGhostRequestToken: number
|
resizeGhostRequestToken: number
|
||||||
requestResizeGhost: () => void
|
requestResizeGhost: () => void
|
||||||
@@ -844,6 +846,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
continuePolygonTarget: null,
|
continuePolygonTarget: null,
|
||||||
currentMousePosition: [0, 0],
|
currentMousePosition: [0, 0],
|
||||||
loadingData: false,
|
loadingData: false,
|
||||||
|
beginContinuePolygonDraft: () => false,
|
||||||
init: (
|
init: (
|
||||||
initEl: HTMLCanvasElement,
|
initEl: HTMLCanvasElement,
|
||||||
initScope: paper.PaperScope,
|
initScope: paper.PaperScope,
|
||||||
@@ -3158,6 +3161,11 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
let continueSourceItems: Array<paper.Path | paper.CompoundPath> = []
|
let continueSourceItems: Array<paper.Path | paper.CompoundPath> = []
|
||||||
let continueTargetContourIndex: number | null = null
|
let continueTargetContourIndex: number | null = null
|
||||||
let continueTargetContext: ContinuePolygonTarget | null = null
|
let continueTargetContext: ContinuePolygonTarget | null = null
|
||||||
|
let continueTargetSnapshot: LabelPathTuple | null = null
|
||||||
|
let continueDraftHints = new Map<
|
||||||
|
string,
|
||||||
|
{ contourIndex: number; points: paper.Point[] }
|
||||||
|
>()
|
||||||
|
|
||||||
const handleCircles = (path: paper.Path | paper.CompoundPath | null) => {
|
const handleCircles = (path: paper.Path | paper.CompoundPath | null) => {
|
||||||
// 更新选中的点
|
// 更新选中的点
|
||||||
@@ -3243,6 +3251,146 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getContinueCandidatePaths = (objectId: number) => {
|
||||||
|
const flattenedPaths: paper.Path[] = []
|
||||||
|
const visitedPaths = new Set<paper.Path>()
|
||||||
|
|
||||||
|
usePaperStore
|
||||||
|
.getState()
|
||||||
|
.getItemsById(objectId)
|
||||||
|
.filter((item) => item.data?.type === "polygon")
|
||||||
|
.forEach((item) => {
|
||||||
|
if (item instanceof paper.CompoundPath) {
|
||||||
|
;(item.children as paper.Path[]).forEach((child) => {
|
||||||
|
if (visitedPaths.has(child)) return
|
||||||
|
visitedPaths.add(child)
|
||||||
|
flattenedPaths.push(child)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (item instanceof paper.Path && !visitedPaths.has(item)) {
|
||||||
|
visitedPaths.add(item)
|
||||||
|
flattenedPaths.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const candidatePaths = flattenedPaths.filter(
|
||||||
|
(path) => path.segments?.length > 3
|
||||||
|
)
|
||||||
|
if (!candidatePaths.length) return [] as paper.Path[]
|
||||||
|
|
||||||
|
const preferredPaths = candidatePaths.filter(
|
||||||
|
(path) => !path.data?.isHollowPolygon
|
||||||
|
)
|
||||||
|
return preferredPaths.length ? preferredPaths : candidatePaths
|
||||||
|
}
|
||||||
|
|
||||||
|
const clonePaperPoints = (pathPoints: paper.Point[]) => {
|
||||||
|
return pathPoints.map((point) => new paper.Point(point))
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeDuplicatedClosingPoint = (pathPoints: paper.Point[]) => {
|
||||||
|
if (pathPoints.length <= 1) return pathPoints
|
||||||
|
const firstPoint = pathPoints[0]
|
||||||
|
const lastPoint = pathPoints[pathPoints.length - 1]
|
||||||
|
if (
|
||||||
|
Math.abs(firstPoint.x - lastPoint.x) < 0.000001 &&
|
||||||
|
Math.abs(firstPoint.y - lastPoint.y) < 0.000001
|
||||||
|
) {
|
||||||
|
pathPoints.pop()
|
||||||
|
}
|
||||||
|
return pathPoints
|
||||||
|
}
|
||||||
|
|
||||||
|
const getContinueContourPoints = (segments: any[] | undefined) => {
|
||||||
|
return removeDuplicatedClosingPoint(
|
||||||
|
(segments || [])
|
||||||
|
.map((segment) => getPointFromSegment(segment))
|
||||||
|
.filter((point): point is paper.Point => point !== null)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getContinueDraftHintKey = (target: ContinuePolygonTarget) => {
|
||||||
|
return `${target.imageId}::${target.operationId}::${target.objectId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeComparableContour = (pathPoints: paper.Point[]) => {
|
||||||
|
return removeDuplicatedClosingPoint(clonePaperPoints(pathPoints)).map(
|
||||||
|
(point) => [point.x, point.y] as [number, number]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSameComparablePoint = (
|
||||||
|
pointA: [number, number],
|
||||||
|
pointB: [number, number]
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
Math.abs(pointA[0] - pointB[0]) < 0.000001 &&
|
||||||
|
Math.abs(pointA[1] - pointB[1]) < 0.000001
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSameContourOrder = (
|
||||||
|
contourA: [number, number][],
|
||||||
|
contourB: [number, number][]
|
||||||
|
) => {
|
||||||
|
if (contourA.length !== contourB.length) return false
|
||||||
|
if (!contourA.length) return false
|
||||||
|
for (let offset = 0; offset < contourB.length; offset += 1) {
|
||||||
|
let matched = true
|
||||||
|
for (let index = 0; index < contourA.length; index += 1) {
|
||||||
|
if (
|
||||||
|
!isSameComparablePoint(
|
||||||
|
contourA[index],
|
||||||
|
contourB[(index + offset) % contourB.length]
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
matched = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matched) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSameContour = (pathA: paper.Point[], pathB: paper.Point[]) => {
|
||||||
|
const contourA = normalizeComparableContour(pathA)
|
||||||
|
const contourB = normalizeComparableContour(pathB)
|
||||||
|
if (contourA.length !== contourB.length || !contourA.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
isSameContourOrder(contourA, contourB) ||
|
||||||
|
isSameContourOrder(contourA, [...contourB].reverse())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const findMatchingContinueContourIndex = (
|
||||||
|
candidatePoints: paper.Point[],
|
||||||
|
contours: any[]
|
||||||
|
) => {
|
||||||
|
for (let index = contours.length - 1; index >= 0; index -= 1) {
|
||||||
|
const contourPoints = getContinueContourPoints(contours[index])
|
||||||
|
if (isSameContour(candidatePoints, contourPoints)) {
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
const setContinueDraftHint = (
|
||||||
|
target: ContinuePolygonTarget,
|
||||||
|
contourIndex: number,
|
||||||
|
contourPoints: paper.Point[]
|
||||||
|
) => {
|
||||||
|
if (contourIndex < 0 || !contourPoints.length) return
|
||||||
|
continueDraftHints.set(getContinueDraftHintKey(target), {
|
||||||
|
contourIndex,
|
||||||
|
points: removeDuplicatedClosingPoint(clonePaperPoints(contourPoints)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const clearContinueSourceItems = (restoreVisible: boolean) => {
|
const clearContinueSourceItems = (restoreVisible: boolean) => {
|
||||||
continueSourceItems.forEach((item) => {
|
continueSourceItems.forEach((item) => {
|
||||||
if (restoreVisible) {
|
if (restoreVisible) {
|
||||||
@@ -3258,18 +3406,26 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
clearContinueSourceItems(restoreVisible)
|
clearContinueSourceItems(restoreVisible)
|
||||||
continueTargetContourIndex = null
|
continueTargetContourIndex = null
|
||||||
continueTargetContext = null
|
continueTargetContext = null
|
||||||
|
continueTargetSnapshot = null
|
||||||
usePaperStore.getState().setContinuePolygonTarget(null)
|
usePaperStore.getState().setContinuePolygonTarget(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasContinuePolygonDraft = () => {
|
||||||
|
return !!(
|
||||||
|
continueTargetContext ||
|
||||||
|
continueTargetSnapshot ||
|
||||||
|
usePaperStore.getState().continuePolygonTarget
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const startContinuePolygonDraft = () => {
|
const startContinuePolygonDraft = () => {
|
||||||
const target = usePaperStore.getState().continuePolygonTarget
|
const target = usePaperStore.getState().continuePolygonTarget
|
||||||
if (!target) return false
|
if (!target) return false
|
||||||
|
|
||||||
const imageLabel = useLabelStore.getState().label.get(target.imageId)
|
const imageLabel = useLabelStore.getState().label.get(target.imageId)
|
||||||
const operationPaths = imageLabel?.get(target.operationId) || []
|
const operationPaths = imageLabel?.get(target.operationId) || []
|
||||||
const targetTuple = operationPaths.find(
|
const targetTuple =
|
||||||
(path) => path[0] === target.objectId
|
operationPaths.find((path) => path[0] === target.objectId) || null
|
||||||
)
|
|
||||||
if (!targetTuple) {
|
if (!targetTuple) {
|
||||||
notifications.show({
|
notifications.show({
|
||||||
color: "yellow",
|
color: "yellow",
|
||||||
@@ -3280,14 +3436,71 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const nextPoints = safeClone(targetTuple[1] || [])
|
const nextPoints = safeClone(targetTuple[1] || [])
|
||||||
|
const continuePaths = getContinueCandidatePaths(target.objectId)
|
||||||
|
const continuePath = continuePaths[continuePaths.length - 1]
|
||||||
|
const continuePathPoints = continuePath?.segments?.length
|
||||||
|
? removeDuplicatedClosingPoint(
|
||||||
|
continuePath.segments.map(
|
||||||
|
(segment) => new paper.Point(segment.point)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
const continueHint = continueDraftHints.get(
|
||||||
|
getContinueDraftHintKey(target)
|
||||||
|
)
|
||||||
|
|
||||||
let contourIndex = -1
|
let contourIndex = -1
|
||||||
for (let i = nextPoints.length - 1; i >= 0; i -= 1) {
|
if (
|
||||||
if (nextPoints[i]?.length > 3) {
|
continueHint &&
|
||||||
|
continueHint.contourIndex >= 0 &&
|
||||||
|
continueHint.contourIndex < nextPoints.length
|
||||||
|
) {
|
||||||
|
contourIndex = continueHint.contourIndex
|
||||||
|
} else if (continuePathPoints.length) {
|
||||||
|
contourIndex = findMatchingContinueContourIndex(
|
||||||
|
continuePathPoints,
|
||||||
|
nextPoints
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (
|
||||||
|
let i = nextPoints.length - 1;
|
||||||
|
contourIndex === -1 && i >= 0;
|
||||||
|
i -= 1
|
||||||
|
) {
|
||||||
|
if (nextPoints[i]?.length > 2) {
|
||||||
contourIndex = i
|
contourIndex = i
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (contourIndex === -1) {
|
if (contourIndex === -1 && nextPoints.length) {
|
||||||
|
contourIndex = nextPoints.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let sourceContourPoints = continueHint?.points?.length
|
||||||
|
? clonePaperPoints(continueHint.points)
|
||||||
|
: []
|
||||||
|
|
||||||
|
if (
|
||||||
|
sourceContourPoints.length &&
|
||||||
|
continuePathPoints.length &&
|
||||||
|
!isSameContour(sourceContourPoints, continuePathPoints)
|
||||||
|
) {
|
||||||
|
sourceContourPoints = []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sourceContourPoints.length && continuePathPoints.length) {
|
||||||
|
sourceContourPoints = clonePaperPoints(continuePathPoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sourceContourPoints.length && contourIndex >= 0) {
|
||||||
|
sourceContourPoints = getContinueContourPoints(nextPoints[contourIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
const contourPoints = sourceContourPoints
|
||||||
|
.slice(0, -1)
|
||||||
|
.map((point) => new paper.Point(point))
|
||||||
|
|
||||||
|
if (!sourceContourPoints.length) {
|
||||||
notifications.show({
|
notifications.show({
|
||||||
color: "yellow",
|
color: "yellow",
|
||||||
message: "当前对象没有可继续标注的区域",
|
message: "当前对象没有可继续标注的区域",
|
||||||
@@ -3296,17 +3509,10 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const contourSegments = safeClone(nextPoints[contourIndex] || [])
|
if (contourPoints.length < 2) {
|
||||||
const continueSegments = contourSegments.slice().reverse()
|
|
||||||
continueSegments.pop()
|
|
||||||
const contourPoints = continueSegments
|
|
||||||
.map((segment: any) => getPointFromSegment(segment))
|
|
||||||
.filter((point: paper.Point | null): point is paper.Point => !!point)
|
|
||||||
|
|
||||||
if (contourPoints.length < 3) {
|
|
||||||
notifications.show({
|
notifications.show({
|
||||||
color: "yellow",
|
color: "yellow",
|
||||||
message: "当前区域至少保留3个节点,无法继续撤回",
|
message: "当前区域至少保留2个节点,无法继续撤回",
|
||||||
})
|
})
|
||||||
clearContinuePolygonContext(true)
|
clearContinuePolygonContext(true)
|
||||||
return false
|
return false
|
||||||
@@ -3321,8 +3527,19 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
item.visible = false
|
item.visible = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useOtherToolsStore.getState().setShowContextMenu({
|
||||||
|
showContextMenu: false,
|
||||||
|
position: { top: 0, left: 0 },
|
||||||
|
imageId: target.imageId,
|
||||||
|
operationId: target.operationId,
|
||||||
|
id: target.objectId,
|
||||||
|
})
|
||||||
|
usePaperSupportStore.getState().clearAll()
|
||||||
|
useObjectStore.getState().updateSelectedPath(target.imageId, "ADD")
|
||||||
|
|
||||||
continueTargetContourIndex = contourIndex
|
continueTargetContourIndex = contourIndex
|
||||||
continueTargetContext = target
|
continueTargetContext = target
|
||||||
|
continueTargetSnapshot = safeClone(targetTuple) as LabelPathTuple
|
||||||
points = contourPoints
|
points = contourPoints
|
||||||
draftId = target.objectId
|
draftId = target.objectId
|
||||||
polygon?.remove()
|
polygon?.remove()
|
||||||
@@ -3351,14 +3568,8 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
const saveContinuePolygonDraft = (draftPath: paper.Path) => {
|
const saveContinuePolygonDraft = (draftPath: paper.Path) => {
|
||||||
const target =
|
const target =
|
||||||
continueTargetContext || usePaperStore.getState().continuePolygonTarget
|
continueTargetContext || usePaperStore.getState().continuePolygonTarget
|
||||||
if (!target) return false
|
const targetTuple = continueTargetSnapshot
|
||||||
|
if (!target || !targetTuple) return false
|
||||||
const imageLabel = useLabelStore.getState().label.get(target.imageId)
|
|
||||||
const operationPaths = imageLabel?.get(target.operationId) || []
|
|
||||||
const targetTuple = operationPaths.find(
|
|
||||||
(path) => path[0] === target.objectId
|
|
||||||
)
|
|
||||||
if (!targetTuple) return false
|
|
||||||
|
|
||||||
const pathData = JSON.parse(draftPath.exportJSON({ precision: 20 }))
|
const pathData = JSON.parse(draftPath.exportJSON({ precision: 20 }))
|
||||||
const nextContour = pathData?.[1]?.segments
|
const nextContour = pathData?.[1]?.segments
|
||||||
@@ -3382,6 +3593,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
safeClone(targetTuple[2] || initialDetail),
|
safeClone(targetTuple[2] || initialDetail),
|
||||||
safeClone(targetTuple[3] || []),
|
safeClone(targetTuple[3] || []),
|
||||||
])
|
])
|
||||||
|
setContinueDraftHint(target, contourIndex, clonePaperPoints(points))
|
||||||
useObjectStore
|
useObjectStore
|
||||||
.getState()
|
.getState()
|
||||||
.updateSelectedPath(target.imageId, "ADD", target.objectId)
|
.updateSelectedPath(target.imageId, "ADD", target.objectId)
|
||||||
@@ -3673,6 +3885,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
|
|
||||||
const points: any[] = []
|
const points: any[] = []
|
||||||
const hollowPoints: any[] = []
|
const hollowPoints: any[] = []
|
||||||
|
let lastOuterContourPoints: paper.Point[] = []
|
||||||
const nextPaths =
|
const nextPaths =
|
||||||
nextPolygon instanceof paper.CompoundPath
|
nextPolygon instanceof paper.CompoundPath
|
||||||
? (nextPolygon.children as paper.Path[])
|
? (nextPolygon.children as paper.Path[])
|
||||||
@@ -3700,6 +3913,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
hollowPoints.push(segments)
|
hollowPoints.push(segments)
|
||||||
} else {
|
} else {
|
||||||
points.push(segments)
|
points.push(segments)
|
||||||
|
lastOuterContourPoints = getContinueContourPoints(segments)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -3728,6 +3942,15 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
prevDetail,
|
prevDetail,
|
||||||
hollowPoints,
|
hollowPoints,
|
||||||
])
|
])
|
||||||
|
setContinueDraftHint(
|
||||||
|
{
|
||||||
|
imageId: activeImage,
|
||||||
|
operationId,
|
||||||
|
objectId: selectedId,
|
||||||
|
},
|
||||||
|
points.length - 1,
|
||||||
|
lastOuterContourPoints
|
||||||
|
)
|
||||||
useObjectStore
|
useObjectStore
|
||||||
.getState()
|
.getState()
|
||||||
.updateSelectedPath(activeImage, "ADD", selectedId)
|
.updateSelectedPath(activeImage, "ADD", selectedId)
|
||||||
@@ -3746,6 +3969,135 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSelectedPolygonAdd = (draftPath: paper.Path) => {
|
||||||
|
const activeImage = useBottomToolsStore.getState().activeImage
|
||||||
|
const selectedIds =
|
||||||
|
useObjectStore.getState().selectedPath[activeImage] || []
|
||||||
|
if (selectedIds.length !== 1) return false
|
||||||
|
|
||||||
|
const selectedId = selectedIds[0]
|
||||||
|
const targetShapes = usePaperStore
|
||||||
|
.getState()
|
||||||
|
.getItemsById(selectedId)
|
||||||
|
.filter((item) => item.data?.type === "polygon")
|
||||||
|
|
||||||
|
const targetShape =
|
||||||
|
targetShapes.find((item) => item instanceof paper.CompoundPath) ||
|
||||||
|
targetShapes.find(
|
||||||
|
(item) => item instanceof paper.Path && !item.data?.isHollowPolygon
|
||||||
|
) ||
|
||||||
|
targetShapes.find((item) => item instanceof paper.Path)
|
||||||
|
|
||||||
|
if (!targetShape) return false
|
||||||
|
|
||||||
|
let clippedDraft: paper.Path | paper.CompoundPath | null =
|
||||||
|
draftPath.intersect(usePaperStore.getState().rasterPath!, {
|
||||||
|
insert: false,
|
||||||
|
trace: false,
|
||||||
|
}) as paper.Path | paper.CompoundPath
|
||||||
|
|
||||||
|
draftPath.remove()
|
||||||
|
|
||||||
|
const draftHasSegments =
|
||||||
|
clippedDraft instanceof paper.CompoundPath
|
||||||
|
? (clippedDraft.children as paper.Path[]).some(
|
||||||
|
(child) => child.segments?.length
|
||||||
|
)
|
||||||
|
: clippedDraft.segments?.length
|
||||||
|
if (!draftHasSegments) {
|
||||||
|
clippedDraft?.remove()
|
||||||
|
clippedDraft = null
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetData = getSafePathData(targetShape.data || {})
|
||||||
|
let nextPolygon = (targetShape as paper.Path | paper.CompoundPath).unite(
|
||||||
|
clippedDraft
|
||||||
|
) as paper.Path | paper.CompoundPath
|
||||||
|
clippedDraft?.remove()
|
||||||
|
clippedDraft = null
|
||||||
|
targetShape.remove()
|
||||||
|
|
||||||
|
const points: any[] = []
|
||||||
|
const hollowPoints: any[] = []
|
||||||
|
let lastOuterContourPoints: paper.Point[] = []
|
||||||
|
const nextPaths =
|
||||||
|
nextPolygon instanceof paper.CompoundPath
|
||||||
|
? (nextPolygon.children as paper.Path[])
|
||||||
|
: ([nextPolygon] as paper.Path[])
|
||||||
|
const nextPathSegments = nextPaths.map((path) => {
|
||||||
|
if (!path.segments?.length) return null
|
||||||
|
const pathData = JSON.parse(path.exportJSON({ precision: 20 }))
|
||||||
|
return pathData?.[1]?.segments || null
|
||||||
|
})
|
||||||
|
const hollowFlags = getHollowFlagsByContainment(
|
||||||
|
nextPathSegments.map((segments) => segments || [])
|
||||||
|
)
|
||||||
|
|
||||||
|
if (nextPolygon instanceof paper.CompoundPath) {
|
||||||
|
nextPolygon.data = Object.assign({}, targetData)
|
||||||
|
}
|
||||||
|
nextPaths.forEach((path, index) => {
|
||||||
|
const segments = nextPathSegments[index]
|
||||||
|
if (!segments?.length) return
|
||||||
|
const isHollowPolygon = !!hollowFlags[index]
|
||||||
|
path.data = Object.assign({}, targetData, {
|
||||||
|
isHollowPolygon,
|
||||||
|
})
|
||||||
|
if (isHollowPolygon) {
|
||||||
|
hollowPoints.push(segments)
|
||||||
|
} else {
|
||||||
|
points.push(segments)
|
||||||
|
lastOuterContourPoints = getContinueContourPoints(segments)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const operationId = (
|
||||||
|
targetData.operationId ||
|
||||||
|
useTopToolsStore.getState().activeOperation ||
|
||||||
|
""
|
||||||
|
).toString()
|
||||||
|
if (!operationId) {
|
||||||
|
nextPolygon.remove()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const prevDetail = safeClone(
|
||||||
|
useLabelStore
|
||||||
|
.getState()
|
||||||
|
.getLabelDetailDataByKeys(activeImage, operationId, selectedId) ||
|
||||||
|
initialDetail
|
||||||
|
)
|
||||||
|
|
||||||
|
if (points.length) {
|
||||||
|
useLabelStore
|
||||||
|
.getState()
|
||||||
|
.setOperation(activeImage, operationId, [
|
||||||
|
selectedId,
|
||||||
|
points,
|
||||||
|
prevDetail,
|
||||||
|
hollowPoints,
|
||||||
|
])
|
||||||
|
setContinueDraftHint(
|
||||||
|
{
|
||||||
|
imageId: activeImage,
|
||||||
|
operationId,
|
||||||
|
objectId: selectedId,
|
||||||
|
},
|
||||||
|
points.length - 1,
|
||||||
|
lastOuterContourPoints
|
||||||
|
)
|
||||||
|
useObjectStore
|
||||||
|
.getState()
|
||||||
|
.updateSelectedPath(activeImage, "ADD", selectedId)
|
||||||
|
} else {
|
||||||
|
nextPolygon.remove()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
forceUpdate()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
polygonTool.onMouseDown = (e: {
|
polygonTool.onMouseDown = (e: {
|
||||||
event: MouseEvent
|
event: MouseEvent
|
||||||
point: paper.Point
|
point: paper.Point
|
||||||
@@ -3756,6 +4108,9 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
if (polygon || points.length) {
|
if (polygon || points.length) {
|
||||||
hidePaperContextMenu()
|
hidePaperContextMenu()
|
||||||
undoPolygonPoint()
|
undoPolygonPoint()
|
||||||
|
if (!points.length && hasContinuePolygonDraft()) {
|
||||||
|
clearContinuePolygonContext(true)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!trySelectPaperObjectByPoint(e.point)) {
|
if (!trySelectPaperObjectByPoint(e.point)) {
|
||||||
@@ -3771,11 +4126,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
usePaperSupportStore.getState().clearAll()
|
usePaperSupportStore.getState().clearAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!polygon && !points.length && hasContinuePolygonDraft()) {
|
||||||
!polygon &&
|
|
||||||
!points.length &&
|
|
||||||
usePaperStore.getState().continuePolygonTarget
|
|
||||||
) {
|
|
||||||
if (!startContinuePolygonDraft()) return
|
if (!startContinuePolygonDraft()) return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3799,7 +4150,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
// 初始化 compoundPolygon
|
// 初始化 compoundPolygon
|
||||||
compoundPolygon = new paper.Path()
|
compoundPolygon = new paper.Path()
|
||||||
|
|
||||||
if (usePaperStore.getState().continuePolygonTarget) {
|
if (hasContinuePolygonDraft()) {
|
||||||
;(polygon as paper.Path).closePath()
|
;(polygon as paper.Path).closePath()
|
||||||
const continueSaved = saveContinuePolygonDraft(
|
const continueSaved = saveContinuePolygonDraft(
|
||||||
polygon as paper.Path
|
polygon as paper.Path
|
||||||
@@ -3841,6 +4192,36 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
useKeyboardStore.getState().drawAction === "add" &&
|
||||||
|
useTopToolsStore.getState().pressA
|
||||||
|
) {
|
||||||
|
const selectedId =
|
||||||
|
useObjectStore.getState().selectedPath[
|
||||||
|
useBottomToolsStore.getState().activeImage
|
||||||
|
]?.[0]
|
||||||
|
const selectedItem = selectedId
|
||||||
|
? usePaperStore.getState().getItemById(selectedId)
|
||||||
|
: null
|
||||||
|
if (selectedItem?.data?.type === "polygon") {
|
||||||
|
;(polygon as paper.Path).closePath()
|
||||||
|
handleSelectedPolygonAdd(polygon as paper.Path)
|
||||||
|
|
||||||
|
selectedCircles.forEach((item) => {
|
||||||
|
item.remove()
|
||||||
|
})
|
||||||
|
selectedCircles = []
|
||||||
|
clearPolygonMagnet()
|
||||||
|
useTopToolsStore.getState().setPressA(false)
|
||||||
|
draftId = null
|
||||||
|
lastPoint = null
|
||||||
|
lastTime = 0
|
||||||
|
points = []
|
||||||
|
polygon = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// pressA 模式下添加多边形
|
// pressA 模式下添加多边形
|
||||||
if (useTopToolsStore.getState().pressA) {
|
if (useTopToolsStore.getState().pressA) {
|
||||||
let ids =
|
let ids =
|
||||||
@@ -3941,11 +4322,15 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
if (polygon.children && polygon.children.length) {
|
if (polygon.children && polygon.children.length) {
|
||||||
let points: any[] = []
|
let points: any[] = []
|
||||||
let hollowPoints: any[] = []
|
let hollowPoints: any[] = []
|
||||||
|
let lastOuterContourPoints: paper.Point[] = []
|
||||||
;(polygon.children as paper.Path[]).forEach((path) => {
|
;(polygon.children as paper.Path[]).forEach((path) => {
|
||||||
let pathData = JSON.parse(path.exportJSON({ precision: 20 }))
|
let pathData = JSON.parse(path.exportJSON({ precision: 20 }))
|
||||||
// 在镂空或分割的区域中 路径方向是false
|
// 在镂空或分割的区域中 路径方向是false
|
||||||
if (path.clockwise) {
|
if (path.clockwise) {
|
||||||
points.push(pathData[1]?.segments)
|
points.push(pathData[1]?.segments)
|
||||||
|
lastOuterContourPoints = getContinueContourPoints(
|
||||||
|
pathData[1]?.segments
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
hollowPoints.push(pathData[1]?.segments)
|
hollowPoints.push(pathData[1]?.segments)
|
||||||
}
|
}
|
||||||
@@ -3981,6 +4366,17 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
hollowPoints,
|
hollowPoints,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
if (points.length) {
|
||||||
|
setContinueDraftHint(
|
||||||
|
{
|
||||||
|
imageId: useBottomToolsStore.getState().activeImage,
|
||||||
|
operationId: useTopToolsStore.getState().activeOperation,
|
||||||
|
objectId: polygon.data.id,
|
||||||
|
},
|
||||||
|
points.length - 1,
|
||||||
|
lastOuterContourPoints
|
||||||
|
)
|
||||||
|
}
|
||||||
if (!useTopToolsStore.getState().pressA)
|
if (!useTopToolsStore.getState().pressA)
|
||||||
useRightToolsStore.getState().pushPathId(polygon.data.id)
|
useRightToolsStore.getState().pushPathId(polygon.data.id)
|
||||||
}
|
}
|
||||||
@@ -4015,6 +4411,15 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
[],
|
[],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
setContinueDraftHint(
|
||||||
|
{
|
||||||
|
imageId: useBottomToolsStore.getState().activeImage,
|
||||||
|
operationId: useTopToolsStore.getState().activeOperation,
|
||||||
|
objectId: polygon.data.id,
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
clonePaperPoints(points)
|
||||||
|
)
|
||||||
useRightToolsStore.getState().pushPathId(polygon.data.id)
|
useRightToolsStore.getState().pushPathId(polygon.data.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4245,11 +4650,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
}
|
}
|
||||||
polygonTool.onKeyDown = (e: paper.KeyEvent) => {
|
polygonTool.onKeyDown = (e: paper.KeyEvent) => {
|
||||||
if (e.key === "escape" || e.key === "alt") {
|
if (e.key === "escape" || e.key === "alt") {
|
||||||
if (
|
if (polygon || points.length || hasContinuePolygonDraft()) {
|
||||||
polygon ||
|
|
||||||
points.length ||
|
|
||||||
usePaperStore.getState().continuePolygonTarget
|
|
||||||
) {
|
|
||||||
resetPolygonDraft()
|
resetPolygonDraft()
|
||||||
clearContinuePolygonContext(true)
|
clearContinuePolygonContext(true)
|
||||||
usePaperSupportStore.getState().handleBufferPaths(null)
|
usePaperSupportStore.getState().handleBufferPaths(null)
|
||||||
@@ -4269,6 +4670,7 @@ export const usePaperStore = create<PaperState>((set) => ({
|
|||||||
set((state: PaperState) => ({
|
set((state: PaperState) => ({
|
||||||
...state,
|
...state,
|
||||||
polygonTool: polygonTool,
|
polygonTool: polygonTool,
|
||||||
|
beginContinuePolygonDraft: () => startContinuePolygonDraft(),
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
addRectangle: (
|
addRectangle: (
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ export const processOpts = [
|
|||||||
// 状态码
|
// 状态码
|
||||||
export const STATUS_CODE = new Map([
|
export const STATUS_CODE = new Map([
|
||||||
[1, "已创建"],
|
[1, "已创建"],
|
||||||
[101, "已创建"], // 审核
|
[101, "已创建待审核"], // 审核
|
||||||
[102, "已创建"], // 复审
|
[102, "已创建待复审"], // 复审
|
||||||
[103, "已创建"], // 审核3
|
[103, "已创建待终审"], // 审核3
|
||||||
[180, "数据同步中"],
|
[180, "数据同步中"],
|
||||||
[190, "Embedding生成中"],
|
[190, "Embedding生成中"],
|
||||||
[191, "Embedding生成失败"],
|
[191, "Embedding生成失败"],
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ export default function AppLayout({
|
|||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
useUserStore.getState().removeUserInfo()
|
useUserStore.getState().removeUserInfo()
|
||||||
|
usePermissionStore.getState().logout()
|
||||||
process.env.NEXT_PUBLIC_OUTPUT_TYPE === "standalone" &&
|
process.env.NEXT_PUBLIC_OUTPUT_TYPE === "standalone" &&
|
||||||
(await deleteCookie())
|
(await deleteCookie())
|
||||||
router.push("/login")
|
router.push("/login")
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ const currentComponentList: MenuItem[] = [
|
|||||||
{
|
{
|
||||||
title: "图片标注",
|
title: "图片标注",
|
||||||
url: "project",
|
url: "project",
|
||||||
icon: "SystemIcon",
|
icon: "ImageAnnotationIcon",
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
title: "我的项目",
|
title: "我的项目",
|
||||||
url: "own",
|
url: "own",
|
||||||
icon: "PersonalMenuIcon",
|
icon: "MyProjectsIcon",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -27,23 +27,23 @@ export const componentList: MenuItem[] = [
|
|||||||
{
|
{
|
||||||
title: "个人中心",
|
title: "个人中心",
|
||||||
url: "person",
|
url: "person",
|
||||||
icon: "PersonalMenuIcon",
|
icon: "PersonalCenterIcon",
|
||||||
},
|
},
|
||||||
...currentComponentList,
|
...currentComponentList,
|
||||||
{
|
{
|
||||||
title: "视频标注",
|
title: "视频标注",
|
||||||
url: "video",
|
url: "video",
|
||||||
icon: "SystemIcon",
|
icon: "VideoAnnotationIcon",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "点云标注",
|
||||||
|
url: "lidar",
|
||||||
|
icon: "LidarAnnotationIcon",
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// title: "点云标注",
|
|
||||||
// url: "lidar",
|
|
||||||
// icon: "SystemIcon",
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
title: "管理中心",
|
title: "管理中心",
|
||||||
url: "mgt",
|
url: "mgt",
|
||||||
icon: "SystemIcon",
|
icon: "ManagementCenterIcon",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,39 @@
|
|||||||
// import { type LucideIcon } from "lucide-react";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AuthMenuIcon,
|
IconCategory2,
|
||||||
PersonalMenuIcon,
|
IconCube3dSphere,
|
||||||
SystemIcon,
|
IconFileText,
|
||||||
TaskMenuIcon,
|
IconFolder,
|
||||||
} from "./icons/custom"
|
IconFolders,
|
||||||
|
IconHierarchy3,
|
||||||
|
IconLayoutDashboard,
|
||||||
|
IconPhoto,
|
||||||
|
IconSettings,
|
||||||
|
IconUserCircle,
|
||||||
|
IconUsersGroup,
|
||||||
|
IconVideo,
|
||||||
|
} from "@tabler/icons-react"
|
||||||
|
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
||||||
|
const iconMap = {
|
||||||
|
DashboardIcon: IconLayoutDashboard,
|
||||||
|
EmployeeManagementIcon: IconUsersGroup,
|
||||||
|
ImageAnnotationIcon: IconPhoto,
|
||||||
|
LidarAnnotationIcon: IconCube3dSphere,
|
||||||
|
ManagementCenterIcon: IconSettings,
|
||||||
|
MyProjectsIcon: IconFolder,
|
||||||
|
PersonalCenterIcon: IconUserCircle,
|
||||||
|
ProjectCategoryIcon: IconCategory2,
|
||||||
|
ProjectManagementIcon: IconFolders,
|
||||||
|
ReportIcon: IconFileText,
|
||||||
|
TeamManagementIcon: IconHierarchy3,
|
||||||
|
VideoAnnotationIcon: IconVideo,
|
||||||
|
SystemIcon: IconSettings,
|
||||||
|
PersonalMenuIcon: IconUserCircle,
|
||||||
|
TaskMenuIcon: IconLayoutDashboard,
|
||||||
|
AuthMenuIcon: IconFileText,
|
||||||
|
} as const
|
||||||
|
|
||||||
export const ClientIcon = React.forwardRef<
|
export const ClientIcon = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.ComponentProps<"button"> & {
|
React.ComponentProps<"button"> & {
|
||||||
@@ -16,12 +41,7 @@ export const ClientIcon = React.forwardRef<
|
|||||||
}
|
}
|
||||||
>(({ icon, ...props }, _ref) => {
|
>(({ icon, ...props }, _ref) => {
|
||||||
if (icon) {
|
if (icon) {
|
||||||
const IconComponent = {
|
const IconComponent = iconMap[icon as keyof typeof iconMap]
|
||||||
SystemIcon,
|
|
||||||
PersonalMenuIcon,
|
|
||||||
TaskMenuIcon,
|
|
||||||
AuthMenuIcon,
|
|
||||||
}[icon]
|
|
||||||
return IconComponent ? <IconComponent style={{ ...props.style }} /> : null
|
return IconComponent ? <IconComponent style={{ ...props.style }} /> : null
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
useProjectStore,
|
useProjectStore,
|
||||||
} from "@/components/label/store/auth"
|
} from "@/components/label/store/auth"
|
||||||
import {
|
import {
|
||||||
|
ALL_CODE,
|
||||||
projectLabelTypeOpts,
|
projectLabelTypeOpts,
|
||||||
projectStatusOpts,
|
projectStatusOpts,
|
||||||
STATUS_CODE,
|
STATUS_CODE,
|
||||||
@@ -36,7 +37,6 @@ import {
|
|||||||
Modal,
|
Modal,
|
||||||
MultiSelect,
|
MultiSelect,
|
||||||
Pagination,
|
Pagination,
|
||||||
Paper,
|
|
||||||
Select,
|
Select,
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
Stack,
|
Stack,
|
||||||
@@ -46,6 +46,15 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
UnstyledButton,
|
UnstyledButton,
|
||||||
} from "@mantine/core"
|
} from "@mantine/core"
|
||||||
|
import {
|
||||||
|
SettingContentPanel,
|
||||||
|
SettingFilterActions,
|
||||||
|
SettingFilterPanel,
|
||||||
|
SettingHeaderActions,
|
||||||
|
SettingListHeader,
|
||||||
|
SettingPage,
|
||||||
|
SettingPanel,
|
||||||
|
} from "@/components/setting/PageSurface"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { notifications } from "@mantine/notifications"
|
import { notifications } from "@mantine/notifications"
|
||||||
import {
|
import {
|
||||||
@@ -134,6 +143,7 @@ export default function ProjectInfoPage({
|
|||||||
type: "dynamic" | "collect" | ""
|
type: "dynamic" | "collect" | ""
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const storeType = type || "all"
|
||||||
|
|
||||||
const { userOpts } = useAllUserStore()
|
const { userOpts } = useAllUserStore()
|
||||||
const userName = usePermissionStore((s) => s.user_name)
|
const userName = usePermissionStore((s) => s.user_name)
|
||||||
@@ -150,20 +160,20 @@ export default function ProjectInfoPage({
|
|||||||
}: any = useProjectStore()
|
}: any = useProjectStore()
|
||||||
|
|
||||||
const storeParams = useMemo(() => {
|
const storeParams = useMemo(() => {
|
||||||
return type === "collect"
|
return storeType === "collect"
|
||||||
? collectParams
|
? collectParams
|
||||||
: type === "dynamic"
|
: storeType === "dynamic"
|
||||||
? dynamicParams
|
? dynamicParams
|
||||||
: allParams
|
: allParams
|
||||||
}, [allParams, collectParams, dynamicParams, type])
|
}, [allParams, collectParams, dynamicParams, storeType])
|
||||||
|
|
||||||
const total = useMemo(() => {
|
const total = useMemo(() => {
|
||||||
return type === "collect"
|
return storeType === "collect"
|
||||||
? collectTotal
|
? collectTotal
|
||||||
: type === "dynamic"
|
: storeType === "dynamic"
|
||||||
? dynamicTotal
|
? dynamicTotal
|
||||||
: allTotal
|
: allTotal
|
||||||
}, [allTotal, collectTotal, dynamicTotal, type])
|
}, [allTotal, collectTotal, dynamicTotal, storeType])
|
||||||
|
|
||||||
const [typeOpts, setTypeOpts] = useState<
|
const [typeOpts, setTypeOpts] = useState<
|
||||||
Array<{ label: string; value: string }>
|
Array<{ label: string; value: string }>
|
||||||
@@ -179,7 +189,7 @@ export default function ProjectInfoPage({
|
|||||||
status: "",
|
status: "",
|
||||||
owner: "",
|
owner: "",
|
||||||
label_type: "",
|
label_type: "",
|
||||||
admin_user: "",
|
admin_user: [] as string[],
|
||||||
start_date: dayjs().subtract(6, "day").format("YYYY-MM-DD"),
|
start_date: dayjs().subtract(6, "day").format("YYYY-MM-DD"),
|
||||||
end_date: dayjs().format("YYYY-MM-DD"),
|
end_date: dayjs().format("YYYY-MM-DD"),
|
||||||
}
|
}
|
||||||
@@ -200,9 +210,9 @@ export default function ProjectInfoPage({
|
|||||||
|
|
||||||
const queryParams = useMemo(() => {
|
const queryParams = useMemo(() => {
|
||||||
const base =
|
const base =
|
||||||
type === "collect"
|
storeType === "collect"
|
||||||
? { ...initCollectParams }
|
? { ...initCollectParams }
|
||||||
: type === "dynamic"
|
: storeType === "dynamic"
|
||||||
? { ...initDynamicParams }
|
? { ...initDynamicParams }
|
||||||
: { ...initAllParams }
|
: { ...initAllParams }
|
||||||
|
|
||||||
@@ -214,7 +224,9 @@ export default function ProjectInfoPage({
|
|||||||
project_name: appliedFilters.project_name || undefined,
|
project_name: appliedFilters.project_name || undefined,
|
||||||
project_type: appliedFilters.project_type || undefined,
|
project_type: appliedFilters.project_type || undefined,
|
||||||
owner: appliedFilters.owner || undefined,
|
owner: appliedFilters.owner || undefined,
|
||||||
admin_user: appliedFilters.admin_user || undefined,
|
admin_user: appliedFilters.admin_user?.length
|
||||||
|
? appliedFilters.admin_user.map((n) => Number(n))
|
||||||
|
: undefined,
|
||||||
start_date: appliedFilters.start_date || undefined,
|
start_date: appliedFilters.start_date || undefined,
|
||||||
end_date: appliedFilters.end_date || undefined,
|
end_date: appliedFilters.end_date || undefined,
|
||||||
label_type: appliedFilters.label_type
|
label_type: appliedFilters.label_type
|
||||||
@@ -222,11 +234,11 @@ export default function ProjectInfoPage({
|
|||||||
: undefined,
|
: undefined,
|
||||||
// 0: 图片标注 1: 点云标注 2: 视频标注
|
// 0: 图片标注 1: 点云标注 2: 视频标注
|
||||||
project_data_type: 0,
|
project_data_type: 0,
|
||||||
status: parseStatusValue(appliedFilters.status),
|
status: parseStatusValue(appliedFilters.status) ?? [...ALL_CODE],
|
||||||
}
|
}
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}, [appliedFilters, storeParams, type])
|
}, [appliedFilters, storeParams, storeType])
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
if (!queryTrigger) return
|
if (!queryTrigger) return
|
||||||
@@ -234,10 +246,10 @@ export default function ProjectInfoPage({
|
|||||||
try {
|
try {
|
||||||
const res = await getProjectList(queryParams)
|
const res = await getProjectList(queryParams)
|
||||||
setRecords(res?.project_list ?? [])
|
setRecords(res?.project_list ?? [])
|
||||||
setTotal(res?.total_items ?? 0, type)
|
setTotal(res?.total_items ?? 0, storeType)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setRecords([])
|
setRecords([])
|
||||||
setTotal(0, type)
|
setTotal(0, storeType)
|
||||||
notifications.show({
|
notifications.show({
|
||||||
color: "red",
|
color: "red",
|
||||||
title: "加载失败",
|
title: "加载失败",
|
||||||
@@ -246,7 +258,7 @@ export default function ProjectInfoPage({
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [queryParams, queryTrigger, setTotal, type])
|
}, [queryParams, queryTrigger, setTotal, storeType])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load()
|
load()
|
||||||
@@ -345,11 +357,10 @@ export default function ProjectInfoPage({
|
|||||||
const canDoAction = !!actionKey
|
const canDoAction = !!actionKey
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper
|
<SettingPanel
|
||||||
key={record.id}
|
key={record.id}
|
||||||
withBorder
|
|
||||||
radius="md"
|
radius="md"
|
||||||
shadow="sm"
|
shadow="xs"
|
||||||
style={{
|
style={{
|
||||||
height: 160,
|
height: 160,
|
||||||
padding: 14,
|
padding: 14,
|
||||||
@@ -357,6 +368,9 @@ export default function ProjectInfoPage({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
|
borderColor: "#E8EDF3",
|
||||||
|
background:
|
||||||
|
"linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(247,250,252,0.96) 100%)",
|
||||||
}}>
|
}}>
|
||||||
<Group justify="space-between" wrap="nowrap" align="flex-start">
|
<Group justify="space-between" wrap="nowrap" align="flex-start">
|
||||||
<Flex flex={1}>
|
<Flex flex={1}>
|
||||||
@@ -551,7 +565,7 @@ export default function ProjectInfoPage({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</SettingPanel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -604,19 +618,12 @@ export default function ProjectInfoPage({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack w="100%" h="calc(100vh - 56px)" p="md" gap="md">
|
<SettingPage>
|
||||||
<Paper withBorder p="md" radius="md">
|
<SettingFilterPanel>
|
||||||
<Group justify="space-between" wrap="wrap" gap="sm">
|
<Group justify="space-between" wrap="wrap" gap="sm">
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<Text fw={700}>
|
<Text fw={700}>筛选条件</Text>
|
||||||
{type === "collect"
|
|
||||||
? "我的收藏"
|
|
||||||
: type === "dynamic"
|
|
||||||
? "动态项目"
|
|
||||||
: "我的项目"}
|
|
||||||
</Text>
|
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
|
||||||
variant="transparent"
|
variant="transparent"
|
||||||
rightSection={
|
rightSection={
|
||||||
searchExpanded ? (
|
searchExpanded ? (
|
||||||
@@ -629,21 +636,19 @@ export default function ProjectInfoPage({
|
|||||||
{searchExpanded ? "收起筛选" : "展开筛选"}
|
{searchExpanded ? "收起筛选" : "展开筛选"}
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
<Group gap="xs">
|
<SettingFilterActions mt={0}>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
variant="default"
|
variant="default"
|
||||||
leftSection={<IconRefresh size={16} />}
|
leftSection={<IconRefresh size={16} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
resetParams(type)
|
resetParams(storeType)
|
||||||
const next = {
|
const next = {
|
||||||
project_name: "",
|
project_name: "",
|
||||||
project_type: "",
|
project_type: "",
|
||||||
status: "",
|
status: "",
|
||||||
owner: "",
|
owner: "",
|
||||||
label_type: "",
|
label_type: "",
|
||||||
admin_user: "",
|
admin_user: [],
|
||||||
start_date: dayjs().subtract(6, "day").format("YYYY-MM-DD"),
|
start_date: dayjs().subtract(6, "day").format("YYYY-MM-DD"),
|
||||||
end_date: dayjs().format("YYYY-MM-DD"),
|
end_date: dayjs().format("YYYY-MM-DD"),
|
||||||
}
|
}
|
||||||
@@ -654,17 +659,15 @@ export default function ProjectInfoPage({
|
|||||||
重置
|
重置
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
leftSection={<IconSearch size={16} />}
|
leftSection={<IconSearch size={16} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSearchParams({ ...storeParams, page_number: 1 }, type)
|
setSearchParams({ ...storeParams, page_number: 1 }, storeType)
|
||||||
setAppliedFilters(filters)
|
setAppliedFilters(filters)
|
||||||
setQueryTrigger((v) => v + 1)
|
setQueryTrigger((v) => v + 1)
|
||||||
}}>
|
}}>
|
||||||
查询
|
查询
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</SettingFilterActions>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Collapse
|
<Collapse
|
||||||
@@ -676,8 +679,6 @@ export default function ProjectInfoPage({
|
|||||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 3, lg: 4 }} spacing="sm">
|
<SimpleGrid cols={{ base: 1, sm: 2, md: 3, lg: 4 }} spacing="sm">
|
||||||
<TextInput
|
<TextInput
|
||||||
label="项目名称"
|
label="项目名称"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
value={filters.project_name}
|
value={filters.project_name}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFilters((s) => ({
|
setFilters((s) => ({
|
||||||
@@ -688,8 +689,6 @@ export default function ProjectInfoPage({
|
|||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="所属业务"
|
label="所属业务"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
data={typeOpts}
|
data={typeOpts}
|
||||||
@@ -700,20 +699,16 @@ export default function ProjectInfoPage({
|
|||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="状态"
|
label="状态"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
searchable
|
searchable
|
||||||
data={projectStatusOpts.map((o) => ({
|
data={projectStatusOpts.map((o) => ({
|
||||||
label: o.label,
|
label: o.label,
|
||||||
value: o.label,
|
value: o.value.toString(),
|
||||||
}))}
|
}))}
|
||||||
value={filters.status}
|
value={filters.status}
|
||||||
onChange={(v) => setFilters((s) => ({ ...s, status: v ?? "" }))}
|
onChange={(v) => setFilters((s) => ({ ...s, status: v ?? "" }))}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="负责人"
|
label="负责人"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
value={filters.owner}
|
value={filters.owner}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFilters((s) => ({ ...s, owner: e.currentTarget.value }))
|
setFilters((s) => ({ ...s, owner: e.currentTarget.value }))
|
||||||
@@ -721,8 +716,6 @@ export default function ProjectInfoPage({
|
|||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
label="标注类型"
|
label="标注类型"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
searchable
|
searchable
|
||||||
clearable
|
clearable
|
||||||
data={projectLabelTypeOpts.map((o) => ({
|
data={projectLabelTypeOpts.map((o) => ({
|
||||||
@@ -734,25 +727,19 @@ export default function ProjectInfoPage({
|
|||||||
setFilters((s) => ({ ...s, label_type: v ?? "" }))
|
setFilters((s) => ({ ...s, label_type: v ?? "" }))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Select
|
<MultiSelect
|
||||||
label="标注管理员"
|
label="标注管理员"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
searchable
|
|
||||||
clearable
|
|
||||||
data={userOpts.map((o) => ({
|
data={userOpts.map((o) => ({
|
||||||
label: o.label,
|
label: o.label,
|
||||||
value: String(o.value),
|
value: String(o.value),
|
||||||
}))}
|
}))}
|
||||||
value={filters.admin_user || null}
|
value={filters.admin_user}
|
||||||
onChange={(v) =>
|
onChange={(v) => setFilters((s) => ({ ...s, admin_user: v }))}
|
||||||
setFilters((s) => ({ ...s, admin_user: v ?? "" }))
|
searchable
|
||||||
}
|
clearable
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="开始日期"
|
label="开始日期"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
type="date"
|
type="date"
|
||||||
value={filters.start_date}
|
value={filters.start_date}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -764,8 +751,6 @@ export default function ProjectInfoPage({
|
|||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="结束日期"
|
label="结束日期"
|
||||||
size="xs"
|
|
||||||
radius="xs"
|
|
||||||
type="date"
|
type="date"
|
||||||
value={filters.end_date}
|
value={filters.end_date}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -775,18 +760,36 @@ export default function ProjectInfoPage({
|
|||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</Paper>
|
</SettingFilterPanel>
|
||||||
|
|
||||||
<Paper
|
<SettingContentPanel
|
||||||
withBorder
|
|
||||||
p="md"
|
|
||||||
radius="md"
|
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minHeight: 0,
|
minHeight: 0,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}>
|
}}>
|
||||||
|
<SettingListHeader
|
||||||
|
title={
|
||||||
|
type === "collect"
|
||||||
|
? "我的收藏"
|
||||||
|
: type === "dynamic"
|
||||||
|
? "动态项目"
|
||||||
|
: "全部项目"
|
||||||
|
}
|
||||||
|
count={`${total ?? 0} 条`}
|
||||||
|
actions={
|
||||||
|
<SettingHeaderActions>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
leftSection={<IconRefresh size={16} />}
|
||||||
|
onClick={() => setQueryTrigger((v) => v + 1)}
|
||||||
|
loading={loading}>
|
||||||
|
刷新
|
||||||
|
</Button>
|
||||||
|
</SettingHeaderActions>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@@ -839,19 +842,19 @@ export default function ProjectInfoPage({
|
|||||||
pageSize={pageSize}
|
pageSize={pageSize}
|
||||||
total={total ?? 0}
|
total={total ?? 0}
|
||||||
onChangePage={(p) => {
|
onChangePage={(p) => {
|
||||||
setSearchParams({ ...storeParams, page_number: p }, type)
|
setSearchParams({ ...storeParams, page_number: p }, storeType)
|
||||||
setQueryTrigger((v) => v + 1)
|
setQueryTrigger((v) => v + 1)
|
||||||
}}
|
}}
|
||||||
onChangePageSize={(s) => {
|
onChangePageSize={(s) => {
|
||||||
setSearchParams(
|
setSearchParams(
|
||||||
{ ...storeParams, page_number: 1, page_size: s },
|
{ ...storeParams, page_number: 1, page_size: s },
|
||||||
type
|
storeType
|
||||||
)
|
)
|
||||||
setQueryTrigger((v) => v + 1)
|
setQueryTrigger((v) => v + 1)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</SettingContentPanel>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
opened={infoModalRecord !== null}
|
opened={infoModalRecord !== null}
|
||||||
@@ -1139,6 +1142,6 @@ export default function ProjectInfoPage({
|
|||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Modal>
|
</Modal>
|
||||||
</Stack>
|
</SettingPage>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user