feat(floorplan): 新增机房平面图功能模块

refactor(models): 为Room和Rack模型添加布局相关字段
feat(routes): 实现机房平面图相关API接口
feat(components): 添加平面图编辑器、画布渲染及交互组件
feat(hooks): 实现平面图数据管理和状态管理
feat(pages): 新增机房平面图页面入口
style(components): 优化平面图组件样式和交互体验
This commit is contained in:
zhang1106
2026-04-30 16:26:39 +08:00
parent 4ab5265882
commit 979c28fdcd
34 changed files with 3108 additions and 62 deletions
@@ -0,0 +1,12 @@
import { useContext } from 'react';
import FloorPlanContext from '../../context/FloorPlanContext';
const useFloorPlanContext = () => {
const context = useContext(FloorPlanContext);
if (!context) {
throw new Error('useFloorPlanContext 必须在 FloorPlanProvider 内使用');
}
return context;
};
export default useFloorPlanContext;
@@ -0,0 +1,89 @@
import { useState, useEffect, useCallback } from 'react';
import axios from 'axios';
const useFloorPlanData = (roomId) => {
const [layoutData, setLayoutData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchLayout = useCallback(async () => {
if (!roomId) {
setLayoutData(null);
return;
}
setLoading(true);
setError(null);
try {
const response = await axios.get(`/api/rooms/${roomId}/layout`);
setLayoutData(response.data);
} catch (err) {
setError(err.response?.data?.error || err.message);
setLayoutData(null);
} finally {
setLoading(false);
}
}, [roomId]);
useEffect(() => {
fetchLayout();
}, [fetchLayout]);
const updateRackPosition = useCallback(async (rackId, rowPos, colPos, facing) => {
try {
await axios.put(`/api/racks/${rackId}/position`, { rowPos, colPos, facing });
await fetchLayout();
return true;
} catch (err) {
setError(err.response?.data?.error || err.message);
return false;
}
}, [fetchLayout]);
const batchUpdatePositions = useCallback(async (positions) => {
try {
await axios.put(`/api/rooms/${roomId}/racks-positions`, { positions });
await fetchLayout();
return true;
} catch (err) {
setError(err.response?.data?.error || err.message);
return false;
}
}, [roomId, fetchLayout]);
const updateLayout = useCallback(async (gridRows, gridCols, layoutConfig) => {
try {
await axios.put(`/api/rooms/${roomId}/layout`, { gridRows, gridCols, layoutConfig });
await fetchLayout();
return true;
} catch (err) {
setError(err.response?.data?.error || err.message);
return false;
}
}, [roomId, fetchLayout]);
const initLayout = useCallback(async (gridRows, gridCols, layoutConfig) => {
try {
await axios.post(`/api/rooms/${roomId}/init-layout`, { gridRows, gridCols, layoutConfig });
await fetchLayout();
return true;
} catch (err) {
setError(err.response?.data?.error || err.message);
return false;
}
}, [roomId, fetchLayout]);
return {
layoutData,
loading,
error,
refetch: fetchLayout,
updateRackPosition,
batchUpdatePositions,
updateLayout,
initLayout,
};
};
export default useFloorPlanData;