2026-05-20-23-28-51 项目库映射交互与导入提示优化

This commit is contained in:
2026-05-20 23:44:42 +08:00
parent 67295ddd9f
commit dcd6fe56c7
8 changed files with 427 additions and 106 deletions

View File

@@ -1257,7 +1257,7 @@ interface PlaneSegment {
b: Point2D;
}
interface OverlayStats {
export interface OverlayStats {
activeModules: number;
filledPixels: number;
segmentCount: number;
@@ -1881,6 +1881,7 @@ export function VoxelizationMappingView({
variant = 'workspace',
toolbar,
overlayPlacement,
onOverlayStatsChange,
}: {
project: Project | null;
moduleStyles: Record<string, ModuleStyle>;
@@ -1893,7 +1894,8 @@ export function VoxelizationMappingView({
rotation: number;
variant?: 'workspace' | 'library';
toolbar?: React.ReactNode;
overlayPlacement?: 'bottom' | 'side';
overlayPlacement?: 'bottom' | 'side' | 'none';
onOverlayStatsChange?: (stats: OverlayStats, visibleModuleCount: number) => void;
}) {
const baseCanvasRef = useRef<HTMLCanvasElement | null>(null);
const overlayCanvasRef = useRef<HTMLCanvasElement | null>(null);
@@ -1918,6 +1920,10 @@ export function VoxelizationMappingView({
const isLibraryVariant = variant === 'library';
const activeOverlayPlacement = overlayPlacement ?? (isLibraryVariant ? 'side' : 'bottom');
useEffect(() => {
onOverlayStatsChange?.(overlayStats, visibleModuleCount);
}, [onOverlayStatsChange, overlayStats, visibleModuleCount]);
useEffect(() => {
if (!project?.dicomCount) {
setDicomPreview(null);
@@ -2360,6 +2366,7 @@ export default function ReverseWorkspace({
const workspaceLoadProjectRef = useRef('');
const poseRepeatRef = useRef<{ timeout: number | null; interval: number | null }>({ timeout: null, interval: null });
const poseImportInputRef = useRef<HTMLInputElement | null>(null);
const visualToolbarScrollRef = useRef<HTMLDivElement | null>(null);
const saveToastTimerRef = useRef<number | null>(null);
const savedWorkspaceSnapshotRef = useRef('');
const initialZStretchRef = useRef<{ projectId: string; pending: boolean }>({ projectId: '', pending: false });
@@ -2767,13 +2774,26 @@ export default function ReverseWorkspace({
}
};
const restoreVisualToolbarScroll = (scrollTop: number | null) => {
if (scrollTop === null) {
return;
}
window.requestAnimationFrame(() => {
if (visualToolbarScrollRef.current) {
visualToolbarScrollRef.current.scrollTop = scrollTop;
}
});
};
const nudgeModelPose = (key: ModelPoseKey, delta: number) => {
const scrollTop = visualToolbarScrollRef.current?.scrollTop ?? null;
setModelPose((current) => ({
...current,
[key]: clampPoseValue(key, current[key] + delta),
}));
setSelectedPoseId('custom');
setPoseImportStatus('');
restoreVisualToolbarScroll(scrollTop);
};
const handlePoseInputChange = (key: ModelPoseKey, value: string) => {
@@ -3378,7 +3398,7 @@ export default function ReverseWorkspace({
</div>
<div className="flex-1 bg-white rounded-3xl border border-slate-100 shadow-sm overflow-hidden flex flex-col p-4 gap-4">
<div className="flex-1 overflow-auto space-y-4 pr-1">
<div ref={visualToolbarScrollRef} className="flex-1 overflow-auto space-y-4 pr-1">
<div>
<p className="mb-2 text-[10px] font-bold uppercase tracking-widest text-slate-400"></p>
<div className="grid grid-cols-2 gap-1 rounded-xl bg-slate-100 p-1">
@@ -3511,7 +3531,10 @@ export default function ReverseWorkspace({
<div key={item.key} className="grid grid-cols-[44px_28px_1fr_28px_72px] items-center gap-2 text-[10px] font-bold text-slate-500">
<span>{item.label}</span>
<button
onMouseDown={() => startPoseRepeat(item.key, -poseStepConfig[item.key].step)}
onMouseDown={(event) => {
event.preventDefault();
startPoseRepeat(item.key, -poseStepConfig[item.key].step);
}}
onMouseUp={stopPoseRepeat}
onMouseLeave={stopPoseRepeat}
onTouchStart={(event) => {
@@ -3535,7 +3558,10 @@ export default function ReverseWorkspace({
className="accent-blue-600"
/>
<button
onMouseDown={() => startPoseRepeat(item.key, poseStepConfig[item.key].step)}
onMouseDown={(event) => {
event.preventDefault();
startPoseRepeat(item.key, poseStepConfig[item.key].step);
}}
onMouseUp={stopPoseRepeat}
onMouseLeave={stopPoseRepeat}
onTouchStart={(event) => {