NextJS仓库规划应用中DWG转SVG后无法在网格正常显示的问题求助
NextJS仓库规划应用中DWG转SVG后无法在网格正常显示的问题求助
我现在快急死了!正在开发一个基于NextJS前端+AdonisJS后端的仓库规划应用,用户能在网格上添加地板、墙壁这类元素,还能移动、缩放、拼接它们。原本一切都运行得好好的,直到用户提出要支持DWG文件导入的需求——上传DWG文件后,要能把它当作普通元素放在网格上,支持移动、缩放等操作。
我已经做了一些工作:DWG文件能上传到后端,后端也能把它转成SVG返回给前端,但就是没法在网格上正常显示SVG内容,现在显示的是这样的效果:

我把前端和后端的代码都贴出来,希望有人能帮我看看这到底能不能实现,问题出在哪。对了,DWG文件大概5MB左右。
后端代码:import_controller.ts
const execFileAsync = promisify(execFile) export default class ImportController { public async importDwg({ request, response }: HttpContext) { try { const file = request.file('file') if (!file) return response.badRequest({ message: 'No file uploaded' }) const tmpDir = path.join(process.cwd(), 'tmp') await fs.mkdir(tmpDir, { recursive: true }) const timestamp = Date.now() const dwgPath = path.join(tmpDir, `${timestamp}_${file.clientName}`) await file.move(tmpDir, { name: path.basename(dwgPath) }) // Convert DWG to SVG const dwg2svgPath = '/usr/local/bin/dwg2svg' const { stdout, stderr } = await execFileAsync(dwg2svgPath, [dwgPath], { maxBuffer: 1024 * 1024 * 64, // 64 MB }) console.log(`dwg2svg stderr: ${stderr}`) let svgString = stdout // Fix malformed width/height line svgString = svgString.replace(/width="([^"]+)\s+height="([^"]+)"/, 'width="$1" height="$2"'); // Ensure viewBox exists const viewBoxMatch = svgString.match(/viewBox="([^"]+)"/) if (!viewBoxMatch) { const width = svgString.match(/width="([^"]+)"/)?.[1] ?? '2000' const height = svgString.match(/height="([^"]+)"/)?.[1] ?? '1000' // svgString = svgString.replace(/<svg([^>]*)>/, `<svg$1 viewBox="0 0 ${width} ${height}">`) } // Constrain to iframe view svgString = svgString .replace(/width="[^"]+"/, 'width="100%"') .replace(/height="[^"]+"/, 'height="100%"') await fs.writeFile(path.join(tmpDir, 'debug_output.svg'), svgString) const base64Svg = `data:image/svg+xml;base64,${Buffer.from(svgString).toString('base64')}` return response.ok({ message: 'DWG converted successfully', image: base64Svg, elements: [ { id: Date.now(), type: 'dwg', x: 0, y: 0, width: 800, height: 600, content: base64Svg, originalFileName: file.clientName, } ] }) } catch (error: any) { console.error('Error importing DWG:', error) return response.internalServerError({ message: 'Failed to import DWG', error: error.message, }) } } }
前端代码:warehouse-grid.tsx
"use client"; export const WarehouseGrid = forwardRef< { fitAllElementsToView: () => void }, WarehouseGridProps >(function WarehouseGrid( { selectedTool, setSelectedTool, lang = "sk", is3DMode, elements, onElementsChange, }: WarehouseGridProps, ref ) { return ( <div className="flex flex-col h-full"> {/* Controls section - always visible in both modes */} <div className="flex items-center gap-4 p-2 bg-white border-b"> <div className="flex items-center gap-2"> <Button variant={selectedTool === "move" ? "default" : "ghost"} onClick={() => setSelectedTool("move")} size="icon" title={t(lang, "moveTool")} > <Move className="h-4 w-4" /> </Button> <Button variant={selectedTool === "resize" ? "default" : "ghost"} onClick={() => setSelectedTool("resize")} size="icon" title={t(lang, "resizeTool")} > <Maximize className="h-4 w-4" /> </Button> <Button variant={selectedTool === "pan" ? "default" : "ghost"} onClick={() => setSelectedTool("pan")} size="icon" title={t(lang, "panTool")} > <Hand className="h-4 w-4" /> </Button> <Separator orientation="vertical" className="h-4" /> <Button variant="outline" size="icon" title={t(lang, "zoomOut")} onClick={handleZoomOut} > <Minus className="h-4 w-4" /> </Button> <Button variant="outline" size="icon" title={t(lang, "zoomIn")} onClick={handleZoomIn} > <Plus className="h-4 w-4" /> </Button> <Button variant="outline" size="icon" title={t(lang, "resetZoom")} onClick={handleZoomReset} > <Maximize2 className="h-4 w-4" /> </Button> <Button variant="ghost" size="icon" title={t(lang, "fitToScreen")} onClick={fitAllElementsToView} > <Maximize2 className="h-4 w-4" /> </Button> </div> </div> {/* Main content area */} <div className="flex-1 relative"> {is3DMode ? ( // 3D Mode <div ref={containerRef} className="w-full h-full" /> ) : ( // 2D Mode <div className="flex h-full"> {/* Grid container */} <div ref={gridRef} className={cn( "relative flex-1 bg-gray-50 overflow-hidden", { "cursor-grab": selectedTool === "pan", "cursor-grabbing": isDraggingViewport, "cursor-default": selectedTool === "move" || selectedTool === "resize", } )} onClick={handleGridClick} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp} onMouseLeave={handleMouseUp} onDragOver={handleDragOver} onDrop={handleDrop} > <div style={{ transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.scale})`, transformOrigin: "0 0", width: "100%", height: "100%", position: "absolute", }} > {renderGrid()} {elements.map((element) => { return ( <DraggableWithoutFindDOMNode key={element.id} position={{ x: element.x, y: element.y }} onStop={(e, data) => handleDragStop(element.id, e, data)} bounds="parent" disabled={selectedTool !== "move"} handle=".drag-handle" > <ResizableBox width={element.width} height={element.height} onResizeStop={(e, data) => handleResizeStop(element.id, e, data)} draggableOpts={{ disabled: selectedTool !== "resize" }} handle={ <div className="absolute right-0 bottom-0 w-4 h-4 bg-blue-500 cursor-se-resize rounded-bl" /> } > <div className={cn( "relative border-2 drag-handle", selectedElementIds.includes(element.id) ? "border-blue-500" : "border-gray-200" )} onClick={(e) => handleElementClick(e, element.id)} > {renderElement(element)} </div> </ResizableBox> </DraggableWithoutFindDOMNode> ); })} </div> </div> {/* Properties panel */} {selectedElementIds.length === 1 && ( <div className="w-80 border-l border-gray-200"> {/* 原代码此处内容未完整粘贴 */} </div> )} </div> )} </div> </div> ); });
备注:内容来源于stack exchange,提问作者Marek
相关产品推荐
相关产品推荐

