2026-05-07-18-11-12 增加模型库和可视化工具栏

This commit is contained in:
2026-05-07 18:24:56 +08:00
parent cbac61eabc
commit 796619632b
9 changed files with 467 additions and 51 deletions

View File

@@ -32,6 +32,7 @@ interface ModuleStyle {
visible: boolean;
color: string;
opacity: number;
partId: number;
}
interface ModelPose {
@@ -430,7 +431,7 @@ function NativeStlViewer({
})
.then((payload) => ({
payload,
style: styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true },
style: styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true, partId: 1 },
})),
),
).then((results) => {
@@ -492,7 +493,7 @@ function NativeStlViewer({
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(payload.vertices, 3));
geometry.computeVertexNormals();
const style = styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true };
const style = styles[fileName] ?? { color: '#3b82f6', opacity: 0.72, visible: true, partId: 1 };
const materialOpacity = solidMode ? Math.max(style.opacity, 0.94) : style.opacity;
const mesh = new THREE.Mesh(
geometry,
@@ -629,12 +630,18 @@ function NativeStlViewer({
);
}
export default function ProjectLibrary({ onReverse }: { onReverse: (projId: string) => void }) {
export default function ProjectLibrary({
onReverse,
initialViewMode = 'dicom',
}: {
onReverse: (projId: string) => void;
initialViewMode?: 'dicom' | 'model' | 'mask';
}) {
const [search, setSearch] = useState('');
const [projects, setProjects] = useState<Project[]>([]);
const [loading, setLoading] = useState(true);
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const [viewMode, setViewMode] = useState<'dicom' | 'model' | 'mask'>('dicom');
const [viewMode, setViewMode] = useState<'dicom' | 'model' | 'mask'>(initialViewMode);
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
const [sliceIndex, setSliceIndex] = useState(0);
const [plane, setPlane] = useState<Plane>('axial');
@@ -701,6 +708,10 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
const sliceTotal = dicomPreview?.total ?? selectedProject?.dicomCount ?? 0;
const selectedSolidity = solidityOptions.find((option) => option.id === solidityLevel) ?? solidityOptions[0];
useEffect(() => {
setViewMode(initialViewMode);
}, [initialViewMode]);
useEffect(() => {
const next: Record<string, ModuleStyle> = {};
stlFiles.forEach((fileName, index) => {
@@ -708,6 +719,7 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
visible: true,
color: defaultModuleColors[index % defaultModuleColors.length],
opacity: 0.72,
partId: index + 1,
};
});
setModuleStyles(next);
@@ -767,12 +779,18 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
visible: true,
color: '#3b82f6',
opacity: 0.72,
partId: 1,
...(prev[fileName] ?? {}),
...partial,
},
}));
};
const updateModulePartId = (fileName: string, value: number) => {
const nextId = Math.max(1, Math.min(255, Math.round(Number.isFinite(value) ? value : 1)));
updateModuleStyle(fileName, { partId: nextId });
};
const toggleAllModules = () => {
const nextVisible = !allModulesVisible;
setModuleStyles(prev => {
@@ -782,6 +800,7 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
visible: nextVisible,
color: next[fileName]?.color ?? defaultModuleColors[index % defaultModuleColors.length],
opacity: next[fileName]?.opacity ?? 0.72,
partId: next[fileName]?.partId ?? index + 1,
};
});
return next;
@@ -1340,7 +1359,7 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
<div className="flex-1 overflow-y-auto space-y-2 pr-1 scrollbar-hide">
{stlFiles.map((fileName, i) => {
const name = fileName.replace(/\.stl$/i, '');
const style = moduleStyles[fileName] ?? { visible: true, color: defaultModuleColors[i % defaultModuleColors.length], opacity: 0.72 };
const style = moduleStyles[fileName] ?? { visible: true, color: defaultModuleColors[i % defaultModuleColors.length], opacity: 0.72, partId: i + 1 };
return (
<div
key={fileName}
@@ -1355,7 +1374,21 @@ export default function ProjectLibrary({ onReverse }: { onReverse: (projId: stri
/>
<div className="flex-1 min-w-0">
<p className="text-[11px] font-bold text-slate-700 truncate">{name}</p>
<p className="text-[9px] text-slate-400 truncate">STL | {fileName}</p>
<div className="mt-0.5 flex items-center gap-2">
<p className="min-w-0 flex-1 text-[9px] text-slate-400 truncate">STL | {fileName}</p>
<label className="flex shrink-0 items-center gap-1 text-[9px] font-bold text-slate-400">
ID
<input
type="number"
min="1"
max="255"
value={style.partId}
onChange={(event) => updateModulePartId(fileName, Number(event.target.value))}
onBlur={(event) => updateModulePartId(fileName, Number(event.target.value))}
className="h-5 w-12 rounded border border-slate-200 bg-white px-1 text-[9px] font-mono text-slate-600 outline-none focus:border-blue-400"
/>
</label>
</div>
<div className="mt-2 flex items-center gap-2">
<span className="text-[9px] text-slate-400 shrink-0"></span>
<input