fix: fix upload bug 3d

This commit is contained in:
2025-10-20 20:00:28 +03:00
parent f5142ec95d
commit 1b8fc3d215
9 changed files with 722 additions and 9 deletions

View File

@@ -0,0 +1,143 @@
import React from "react";
import {
Box,
CircularProgress,
Typography,
LinearProgress,
} from "@mui/material";
interface LoadingSpinnerProps {
progress?: number;
message?: string;
size?: number;
color?: "primary" | "secondary" | "error" | "info" | "success" | "warning";
variant?: "circular" | "linear";
showPercentage?: boolean;
thickness?: number;
className?: string;
}
export const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({
progress,
message = "Загрузка...",
size = 40,
color = "primary",
variant = "circular",
showPercentage = true,
thickness = 4,
className,
}) => {
if (variant === "linear") {
return (
<Box
className={className}
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: 2,
padding: 3,
width: "100%",
}}
>
<Box sx={{ width: "100%", maxWidth: 300 }}>
<LinearProgress
variant={progress !== undefined ? "determinate" : "indeterminate"}
value={progress}
color={color}
sx={{
height: 8,
borderRadius: 4,
backgroundColor: "rgba(0, 0, 0, 0.1)",
"& .MuiLinearProgress-bar": {
borderRadius: 4,
},
}}
/>
{showPercentage && progress !== undefined && (
<Typography
variant="caption"
color="text.secondary"
sx={{
display: "block",
textAlign: "center",
mt: 1,
fontSize: "0.875rem",
fontWeight: 500,
}}
>
{`${Math.round(progress)}%`}
</Typography>
)}
</Box>
{message && (
<Typography variant="body2" color="text.secondary" textAlign="center">
{message}
</Typography>
)}
</Box>
);
}
return (
<Box
className={className}
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: 2,
padding: 3,
}}
>
<Box sx={{ position: "relative", display: "inline-flex" }}>
<CircularProgress
size={size}
color={color}
variant={progress !== undefined ? "determinate" : "indeterminate"}
value={progress}
thickness={thickness}
sx={{
"& .MuiCircularProgress-circle": {
strokeLinecap: "round",
},
}}
/>
{showPercentage && progress !== undefined && (
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: "absolute",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Typography
variant="caption"
component="div"
color="text.secondary"
sx={{
fontSize: size * 0.25,
fontWeight: 600,
lineHeight: 1,
}}
>
{`${Math.round(progress)}%`}
</Typography>
</Box>
)}
</Box>
{message && (
<Typography variant="body2" color="text.secondary" textAlign="center">
{message}
</Typography>
)}
</Box>
);
};

View File

@@ -0,0 +1,196 @@
import React from "react";
import {
Box,
Typography,
LinearProgress,
CircularProgress,
} from "@mui/material";
interface ModelLoadingIndicatorProps {
progress?: number;
message?: string;
isVisible?: boolean;
variant?: "overlay" | "inline";
size?: "small" | "medium" | "large";
showDetails?: boolean;
}
export const ModelLoadingIndicator: React.FC<ModelLoadingIndicatorProps> = ({
progress = 0,
message = "Загрузка 3D модели...",
isVisible = true,
variant = "overlay",
size = "medium",
showDetails = true,
}) => {
const sizeConfig = {
small: {
spinnerSize: 32,
fontSize: "0.75rem",
progressBarWidth: 150,
padding: 2,
},
medium: {
spinnerSize: 48,
fontSize: "0.875rem",
progressBarWidth: 200,
padding: 3,
},
large: {
spinnerSize: 64,
fontSize: "1rem",
progressBarWidth: 250,
padding: 4,
},
};
const currentSize = sizeConfig[size];
if (!isVisible) return null;
const getProgressStage = (progress: number): string => {
if (progress < 20) return "Инициализация...";
if (progress < 40) return "Загрузка геометрии...";
if (progress < 60) return "Обработка материалов...";
if (progress < 80) return "Загрузка текстур...";
if (progress < 95) return "Финализация...";
if (progress === 100) return "Готово!";
return "Загрузка...";
};
const content = (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: 2,
padding: currentSize.padding,
textAlign: "center",
width: "100%",
}}
>
{/* Крутяшка с процентами */}
<Box sx={{ position: "relative", display: "inline-flex" }}>
<CircularProgress
size={currentSize.spinnerSize}
variant="determinate"
value={progress}
thickness={4}
sx={{
color: "primary.main",
}}
/>
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: "absolute",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Typography
variant="caption"
component="div"
color="text.secondary"
sx={{
fontSize: currentSize.spinnerSize * 0.25,
fontWeight: 600,
lineHeight: 1,
}}
>
{`${Math.round(progress)}%`}
</Typography>
</Box>
</Box>
{/* Линейный прогресс бар */}
<Box sx={{ width: "100%", maxWidth: currentSize.progressBarWidth }}>
<LinearProgress
variant="determinate"
value={progress}
color="primary"
sx={{
height: 8,
borderRadius: 4,
backgroundColor: "rgba(0, 0, 0, 0.1)",
}}
/>
</Box>
{/* Основное сообщение */}
<Typography
variant="body2"
color="text.secondary"
sx={{
fontSize: currentSize.fontSize,
fontWeight: 500,
maxWidth: 280,
lineHeight: 1.4,
}}
>
{message}
</Typography>
{/* Детальная информация о прогрессе */}
{showDetails && progress > 0 && (
<Typography
variant="caption"
color="text.disabled"
sx={{
fontSize: "0.75rem",
opacity: 0.8,
fontWeight: 400,
}}
>
{getProgressStage(progress)}
</Typography>
)}
</Box>
);
if (variant === "overlay") {
return (
<Box
sx={{
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(255, 255, 255, 0.95)",
display: "flex",
alignItems: "center",
justifyContent: "center",
zIndex: 1000,
borderRadius: 1,
border: "1px solid rgba(0, 0, 0, 0.05)",
}}
>
{content}
</Box>
);
}
return (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
minHeight: 200,
backgroundColor: "rgba(0, 0, 0, 0.02)",
borderRadius: 2,
border: "1px dashed",
borderColor: "divider",
}}
>
{content}
</Box>
);
};