From 130b2ad5f773b0e4a22bb5f2303a198483436b9e Mon Sep 17 00:00:00 2001
From: zhang1106 <849185023@qq.com>
Date: Sat, 20 Dec 2025 17:43:50 +0800
Subject: [PATCH] =?UTF-8?q?feat(=E8=83=8C=E6=99=AF=E8=AE=BE=E7=BD=AE):=20?=
=?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=83=8C=E6=99=AF=E5=9B=BE=E7=89=87=E4=B8=8A?=
=?UTF-8?q?=E4=BC=A0=E5=8F=8A=E8=AE=BE=E7=BD=AE=E5=8A=9F=E8=83=BD-=20?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=83=8C=E6=99=AF=E8=AE=BE=E7=BD=AE=E8=B7=AF?=
=?UTF-8?q?=E7=94=B1=E5=92=8C=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD-=20=E6=B7=BB=E5=8A=A0=E9=9D=99=E6=80=81=E6=96=87?=
=?UTF-8?q?=E4=BB=B6=E6=9C=8D=E5=8A=A1=E6=94=AF=E6=8C=81-=20=E5=89=8D?=
=?UTF-8?q?=E7=AB=AF=E5=AE=9E=E7=8E=B0=E8=83=8C=E6=99=AF=E8=AE=BE=E7=BD=AE?=
=?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=92=8C=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?=
=?UTF-8?q?-=20=E9=85=8D=E7=BD=AEvite=E4=BB=A3=E7=90=86=E4=BB=A5=E6=94=AF?=
=?UTF-8?q?=E6=8C=81=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=AE=BF=E9=97=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
backend/backgroundSettings.json | 5 +
backend/fileIndex.json | 1 +
backend/routes/background.js | 73 +++++++++++++++
backend/server.js | 5 +
frontend/src/pages/RackVisualization.jsx | 112 ++++++++++++++++++-----
frontend/vite.config.js | 4 +
6 files changed, 175 insertions(+), 25 deletions(-)
create mode 100644 backend/backgroundSettings.json
create mode 100644 backend/fileIndex.json
create mode 100644 backend/routes/background.js
diff --git a/backend/backgroundSettings.json b/backend/backgroundSettings.json
new file mode 100644
index 0000000..ffa3dda
--- /dev/null
+++ b/backend/backgroundSettings.json
@@ -0,0 +1,5 @@
+{
+ "type": "image",
+ "image": "/uploads/1766210792626_çææ
ä¾£å¾ç.png",
+ "size": "cover"
+}
\ No newline at end of file
diff --git a/backend/fileIndex.json b/backend/fileIndex.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/backend/fileIndex.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/backend/routes/background.js b/backend/routes/background.js
new file mode 100644
index 0000000..1ec3e40
--- /dev/null
+++ b/backend/routes/background.js
@@ -0,0 +1,73 @@
+const express = require('express');
+const path = require('path');
+const fs = require('fs');
+const router = express.Router();
+
+// 确保上传目录存在
+const UPLOAD_DIR = path.join(__dirname, '../uploads');
+if (!fs.existsSync(UPLOAD_DIR)) {
+ fs.mkdirSync(UPLOAD_DIR, { recursive: true });
+}
+
+// 确保背景设置文件存在
+const SETTINGS_FILE = path.join(__dirname, '../backgroundSettings.json');
+if (!fs.existsSync(SETTINGS_FILE)) {
+ fs.writeFileSync(SETTINGS_FILE, JSON.stringify({
+ type: 'gradient',
+ image: '',
+ size: 'contain'
+ }, null, 2));
+}
+
+// 上传图片接口
+router.post('/upload', (req, res) => {
+ try {
+ if (!req.files || !req.files.file) {
+ return res.status(400).json({ error: '没有上传文件' });
+ }
+
+ const file = req.files.file;
+ const fileName = `${Date.now()}_${file.name}`;
+ const filePath = path.join(UPLOAD_DIR, fileName);
+
+ // 保存文件到服务器
+ file.mv(filePath, (err) => {
+ if (err) {
+ console.error('文件保存失败:', err);
+ return res.status(500).json({ error: '文件保存失败' });
+ }
+
+ // 返回文件路径
+ const fileUrl = `/uploads/${fileName}`;
+ res.json({ path: fileUrl });
+ });
+ } catch (error) {
+ console.error('上传错误:', error);
+ res.status(500).json({ error: '上传失败' });
+ }
+});
+
+// 获取背景设置
+router.get('/settings', (req, res) => {
+ try {
+ const settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf8'));
+ res.json(settings);
+ } catch (error) {
+ console.error('读取背景设置失败:', error);
+ res.status(500).json({ error: '读取背景设置失败' });
+ }
+});
+
+// 保存背景设置
+router.post('/settings', (req, res) => {
+ try {
+ const settings = req.body;
+ fs.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2));
+ res.json({ success: true });
+ } catch (error) {
+ console.error('保存背景设置失败:', error);
+ res.status(500).json({ error: '保存背景设置失败' });
+ }
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/backend/server.js b/backend/server.js
index 139fe01..c1498f8 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -38,12 +38,17 @@ const deviceRoutes = require('./routes/devices');
const rackRoutes = require('./routes/racks');
const roomRoutes = require('./routes/rooms');
const deviceFieldRoutes = require('./routes/deviceFields');
+const backgroundRoutes = require('./routes/background');
// 使用路由
app.use('/api/devices', deviceRoutes);
app.use('/api/racks', rackRoutes);
app.use('/api/rooms', roomRoutes);
app.use('/api/deviceFields', deviceFieldRoutes);
+app.use('/api/background', backgroundRoutes);
+
+// 静态文件服务
+app.use('/uploads', express.static('uploads'));
// 健康检查
app.get('/health', (req, res) => {
diff --git a/frontend/src/pages/RackVisualization.jsx b/frontend/src/pages/RackVisualization.jsx
index 369d77b..3eef36f 100644
--- a/frontend/src/pages/RackVisualization.jsx
+++ b/frontend/src/pages/RackVisualization.jsx
@@ -145,6 +145,7 @@ function RackVisualization() {
const [backgroundImage, setBackgroundImage] = useState(null);
const [backgroundType, setBackgroundType] = useState('gradient'); // gradient or image
const [backgroundSize, setBackgroundSize] = useState('contain'); // cover, contain, auto
+ const [uploading, setUploading] = useState(false); // 上传状态
// 获取所有机柜
const fetchRacks = async () => {
@@ -333,6 +334,7 @@ function RackVisualization() {
useEffect(() => {
fetchRacks();
+ loadBackgroundSettings();
}, []);
// 根据设备类型获取图标
@@ -617,6 +619,33 @@ function RackVisualization() {
message.success('数据已刷新');
});
};
+
+ // 保存背景设置到服务器
+ const saveBackgroundSettings = async (type, image, size) => {
+ try {
+ await axios.post('/api/background/settings', {
+ type,
+ image,
+ size
+ });
+ } catch (error) {
+ console.error('保存背景设置失败:', error);
+ }
+ };
+
+ // 从服务器加载背景设置
+ const loadBackgroundSettings = async () => {
+ try {
+ const response = await axios.get('/api/background/settings');
+ if (response.data) {
+ setBackgroundType(response.data.type || 'gradient');
+ setBackgroundImage(response.data.image || null);
+ setBackgroundSize(response.data.size || 'contain');
+ }
+ } catch (error) {
+ console.error('加载背景设置失败:', error);
+ }
+ };
// 重置视角
const handleResetView = () => {
@@ -689,45 +718,78 @@ function RackVisualization() {
} onClick={handleZoomOut}>缩小
} onClick={handleResetView}>重置视角
- {backgroundType === 'image' && (
-
- setBackgroundImage(e.target.value)}
- value={backgroundImage || ''}
- />
+ placeholder="选择背景类型"
+ style={{ width: 150 }}
+ value={backgroundType}
+ onChange={async (type) => {
+ setBackgroundType(type);
+ await saveBackgroundSettings(type, backgroundImage, backgroundSize);
+ }}
+ >
+
+
+
+ {backgroundType === 'image' && (
+
+ {
+ const image = e.target.value;
+ setBackgroundImage(image);
+ await saveBackgroundSettings(backgroundType, image, backgroundSize);
+ }}
+ value={backgroundImage || ''}
+ />
{
+ onChange={async (e) => {
const file = e.target.files[0];
if (file) {
- const reader = new FileReader();
- reader.onload = (event) => {
- setBackgroundImage(event.target.result);
- };
- reader.readAsDataURL(file);
+ try {
+ setUploading(true);
+ const formData = new FormData();
+ formData.append('file', file);
+ formData.append('type', 'background');
+
+ // 发送图片到服务器
+ const response = await axios.post('/api/background/upload', formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data'
+ }
+ });
+
+ // 保存服务器返回的图片路径
+ if (response.data && response.data.path) {
+ setBackgroundImage(response.data.path);
+ message.success('背景图片上传成功');
+ // 保存背景设置到服务器
+ await saveBackgroundSettings('image', response.data.path, backgroundSize);
+ } else {
+ message.error('上传失败:服务器返回格式不正确');
+ }
+ } catch (error) {
+ console.error('上传失败:', error);
+ message.error('背景图片上传失败,请重试');
+ } finally {
+ setUploading(false);
+ }
}
}}
style={{ display: 'none' }}
id="backgroundFileInput"
/>
-
+