修复盘点失败和机柜统计不准确的问题

This commit is contained in:
root
2026-06-09 16:02:09 +08:00
parent 8c9e82c8a7
commit e7a0cbb9d1
5 changed files with 197 additions and 68 deletions
+92
View File
@@ -0,0 +1,92 @@
# AGENTS.md
## Project Overview
IDC Device Asset Management System — full-stack app for data center device lifecycle management with 3D visualization. Monorepo: `backend/` (Express + Sequelize), `frontend/` (React + Vite + Ant Design + Three.js).
## 交互规则
1. 处理所有问题时,**全程思考过程必须使用中文**(包括需求分析、逻辑拆解、方案选择、步骤推导等所有内部推理环节)
2. 最终输出的所有回答内容(包括文字解释、代码注释、步骤说明等)**必须全部使用中文**,仅代码语法本身的英文关键词除外
## Quick Commands
```bash
# Install all dependencies (root + backend + frontend)
npm run install:all
# Start both backend (port 8000) and frontend (port 3000)
npm start
# Backend only
cd backend && npm run dev # nodemon
# Frontend only
cd frontend && npm run dev # vite
```
## Backend
- **Entry**: `backend/server.js` — initializes DB, syncs models, loads routes
- **DB**: SQLite (default) or MySQL, configured via `backend/.env` (`DB_TYPE`)
- **Routes**: config-driven at `backend/config/routes.js`**adding a route requires editing this array**
- **Auth**: JWT; in dev, `JWT_SECRET` auto-generates if missing. Production requires it set in `.env`.
- **ORM sync**: dev mode uses `alter: true` for business models; production uses safe sync
- **Swagger**: served at `/api-docs`
## Frontend
- **Path alias**: `@/*``src/*` (configured in `jsconfig.json`)
- **Dev proxy**: `/api` and `/uploads``http://localhost:8000`
- **Port**: configurable via `.frontend-port` file or `FRONTEND_PORT` env var (default 3000)
## Testing
```bash
# Backend tests (Jest, in-memory SQLite — NOT the dev database)
cd backend && npm test # --runInBand
cd backend && npm run test:coverage
# Specific test files
cd backend && npx jest tests/operationLog.model.test.js --runInBand
# Frontend: vitest is in devDeps but no test scripts defined
```
Test setup (`backend/tests/setupEnv.js`): sets `NODE_ENV=test`, uses `:memory:` SQLite. Tests run with `force: true` sync.
## Lint & Format
```bash
# Backend
cd backend && npm run lint
cd backend && npm run format
# Frontend
cd frontend && npm run lint
cd frontend && npm run format
# Root-level Prettier config applies to both
```
**Note**: ESLint rules are very relaxed in both packages (many rules off). `lint` exits non-zero on warnings (`--max-warnings 0`).
## Node Version
`.nvmrc` specifies **20.10.0**.
## Project Conventions
- Backend is **CommonJS** (`require`); frontend is **ESM** (`import`)
- Backend logs via Winston (`backend/utils/logger.js`)
- API response pattern: `{ success: boolean, data?: ..., message?: ... }`
- Commit convention: `feat:`, `fix:`, `docs:`, `refactor:`, etc. (see README)
- Frontend uses Zustand for state, SWR for data fetching, Ant Design for UI
## Gotchas
- `npm start` uses `concurrently` to run both services; each must be installed separately first
- `backend/server.js` auto-creates `JWT_SECRET` in `.env` if missing in dev — don't commit the generated secret
- Database files (`*.db`, `*.sqlite`) are gitignored — local dev DB is ephemeral
- `backend/uploads/` and `backend/temp/` have `.gitkeep` files; contents are gitignored
- No CI workflows exist — lint/test must be run manually
+44
View File
@@ -78,6 +78,50 @@ router.get('/', async (req, res) => {
} }
}); });
// 获取机柜统计信息(全量,不受分页影响)
router.get('/stats', async (req, res) => {
try {
const { roomId, status, keyword } = req.query;
const where = {};
if (roomId && roomId !== 'all') {
where.roomId = roomId;
}
if (status && status !== 'all') {
where.status = status;
}
if (keyword) {
where[require('sequelize').Op.or] = [
{ rackId: { [require('sequelize').Op.like]: `%${keyword}%` } },
{ name: { [require('sequelize').Op.like]: `%${keyword}%` } },
];
}
const total = await Rack.count({ where });
const active = await Rack.count({ where: { ...where, status: 'active' } });
const totalPowerResult = await Rack.findAll({
where,
attributes: [[sequelize.fn('COALESCE', sequelize.fn('SUM', sequelize.col('currentPower')), 0), 'totalPower']],
raw: true,
});
const totalPower = parseFloat(totalPowerResult[0]?.totalPower) || 0;
const rackIds = await Rack.findAll({
where,
attributes: ['rackId'],
raw: true,
});
const rackIdList = rackIds.map(r => r.rackId);
const totalDevices = rackIdList.length > 0
? await Device.count({ where: { rackId: { [require('sequelize').Op.in]: rackIdList } } })
: 0;
res.json({ total, active, totalPower, totalDevices });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
const MAX_EXPORT_SIZE = 50000; const MAX_EXPORT_SIZE = 50000;
router.get('/all', async (req, res) => { router.get('/all', async (req, res) => {
+26 -31
View File
@@ -281,7 +281,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5", "@babel/generator": "^7.28.5",
@@ -630,7 +629,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
@@ -674,7 +672,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -696,7 +693,6 @@
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
"integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@emotion/memoize": "^0.9.0" "@emotion/memoize": "^0.9.0"
} }
@@ -1771,7 +1767,6 @@
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.18.0.tgz", "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.18.0.tgz",
"integrity": "sha512-FYZZqD0UUHUswKz3LQl2Z7H24AhD14XGTsIRw3SJaXUxyfVMi+1yiZGmqTcPt/CkPpdU7rrxqcyQ1zJE5DjvIQ==", "integrity": "sha512-FYZZqD0UUHUswKz3LQl2Z7H24AhD14XGTsIRw3SJaXUxyfVMi+1yiZGmqTcPt/CkPpdU7rrxqcyQ1zJE5DjvIQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/runtime": "^7.17.8", "@babel/runtime": "^7.17.8",
"@types/react-reconciler": "^0.26.7", "@types/react-reconciler": "^0.26.7",
@@ -2376,7 +2371,8 @@
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/@types/babel__core": { "node_modules/@types/babel__core": {
"version": "7.20.5", "version": "7.20.5",
@@ -2737,7 +2733,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz",
"integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.2.2" "csstype": "^3.2.2"
} }
@@ -2768,7 +2763,6 @@
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz", "resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz",
"integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==", "integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@dimforge/rapier3d-compat": "~0.12.0", "@dimforge/rapier3d-compat": "~0.12.0",
"@tweenjs/tween.js": "~23.1.3", "@tweenjs/tween.js": "~23.1.3",
@@ -2914,6 +2908,7 @@
"integrity": "sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw==", "integrity": "sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/parser": "^7.29.0", "@babel/parser": "^7.29.0",
"@vue/shared": "3.5.29", "@vue/shared": "3.5.29",
@@ -2928,6 +2923,7 @@
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"peer": true,
"engines": { "engines": {
"node": ">=0.12" "node": ">=0.12"
}, },
@@ -2940,7 +2936,8 @@
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/@vue/compiler-dom": { "node_modules/@vue/compiler-dom": {
"version": "3.5.29", "version": "3.5.29",
@@ -2948,6 +2945,7 @@
"integrity": "sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg==", "integrity": "sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-core": "3.5.29", "@vue/compiler-core": "3.5.29",
"@vue/shared": "3.5.29" "@vue/shared": "3.5.29"
@@ -2977,7 +2975,8 @@
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/@vue/compiler-ssr": { "node_modules/@vue/compiler-ssr": {
"version": "3.5.29", "version": "3.5.29",
@@ -2985,6 +2984,7 @@
"integrity": "sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw==", "integrity": "sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.29", "@vue/compiler-dom": "3.5.29",
"@vue/shared": "3.5.29" "@vue/shared": "3.5.29"
@@ -2996,6 +2996,7 @@
"integrity": "sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA==", "integrity": "sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/shared": "3.5.29" "@vue/shared": "3.5.29"
} }
@@ -3006,6 +3007,7 @@
"integrity": "sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg==", "integrity": "sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/reactivity": "3.5.29", "@vue/reactivity": "3.5.29",
"@vue/shared": "3.5.29" "@vue/shared": "3.5.29"
@@ -3017,6 +3019,7 @@
"integrity": "sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg==", "integrity": "sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/reactivity": "3.5.29", "@vue/reactivity": "3.5.29",
"@vue/runtime-core": "3.5.29", "@vue/runtime-core": "3.5.29",
@@ -3030,6 +3033,7 @@
"integrity": "sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g==", "integrity": "sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-ssr": "3.5.29", "@vue/compiler-ssr": "3.5.29",
"@vue/shared": "3.5.29" "@vue/shared": "3.5.29"
@@ -3043,7 +3047,8 @@
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.29.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.29.tgz",
"integrity": "sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==", "integrity": "sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/@webgpu/types": { "node_modules/@webgpu/types": {
"version": "0.1.69", "version": "0.1.69",
@@ -3057,7 +3062,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -3117,6 +3121,7 @@
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -3516,7 +3521,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.9.0", "baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759", "caniuse-lite": "^1.0.30001759",
@@ -3993,7 +3997,6 @@
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC", "license": "ISC",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@@ -4124,8 +4127,7 @@
"version": "1.11.19", "version": "1.11.19",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
@@ -4240,7 +4242,8 @@
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/dom-helpers": { "node_modules/dom-helpers": {
"version": "5.2.1", "version": "5.2.1",
@@ -4539,7 +4542,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@@ -4600,7 +4602,6 @@
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"eslint-config-prettier": "bin/cli.js" "eslint-config-prettier": "bin/cli.js"
}, },
@@ -5964,7 +5965,6 @@
"integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==", "integrity": "sha512-GtldT42B8+jefDUC4yUKAvsaOrH7PDHmZxZXNgF2xMmymjUbRYJvpAybZAKEmXDGTM0mCsz8duOa4vTm5AY2Kg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@acemir/cssom": "^0.9.28", "@acemir/cssom": "^0.9.28",
"@asamuzakjp/dom-selector": "^6.7.6", "@asamuzakjp/dom-selector": "^6.7.6",
@@ -6179,6 +6179,7 @@
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"lz-string": "bin/bin.js" "lz-string": "bin/bin.js"
} }
@@ -6720,7 +6721,6 @@
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"prettier": "bin/prettier.cjs" "prettier": "bin/prettier.cjs"
}, },
@@ -6750,6 +6750,7 @@
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"ansi-regex": "^5.0.1", "ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0", "ansi-styles": "^5.0.0",
@@ -6765,6 +6766,7 @@
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -6777,7 +6779,8 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/promise-worker-transferable": { "node_modules/promise-worker-transferable": {
"version": "1.0.4", "version": "1.0.4",
@@ -7450,7 +7453,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
}, },
@@ -7475,7 +7477,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0", "loose-envify": "^1.1.0",
"scheduler": "^0.23.2" "scheduler": "^0.23.2"
@@ -8423,7 +8424,6 @@
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"peer": true,
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0", "acorn": "^8.15.0",
@@ -8450,8 +8450,7 @@
"version": "0.183.2", "version": "0.183.2",
"resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz", "resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz",
"integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==", "integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/three-mesh-bvh": { "node_modules/three-mesh-bvh": {
"version": "0.7.8", "version": "0.7.8",
@@ -8974,7 +8973,6 @@
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
"license": "MIT", "license": "MIT",
"peer": true,
"peerDependencies": { "peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
} }
@@ -9003,7 +9001,6 @@
"integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.18.10", "esbuild": "^0.18.10",
"postcss": "^8.4.27", "postcss": "^8.4.27",
@@ -9623,7 +9620,6 @@
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.27.0", "esbuild": "^0.27.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",
@@ -10029,7 +10025,6 @@
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }
+11 -11
View File
@@ -75,8 +75,8 @@ const InventoryTaskExecution = () => {
setLoading(true); setLoading(true);
try { try {
const res = await api.get(`/inventory/plans/${planId}`); const res = await api.get(`/inventory/plans/${planId}`);
setPlan(res.data.plan); setPlan(res.plan);
setTasks(res.data.tasks || []); setTasks(res.tasks || []);
} catch (error) { } catch (error) {
message.error('获取盘点计划失败'); message.error('获取盘点计划失败');
} finally { } finally {
@@ -87,9 +87,9 @@ const InventoryTaskExecution = () => {
const fetchTaskRecords = async taskId => { const fetchTaskRecords = async taskId => {
try { try {
const res = await api.get(`/inventory/tasks/${taskId}`); const res = await api.get(`/inventory/tasks/${taskId}`);
setCurrentTask(res.data.task); setCurrentTask(res.task);
setRecords(res.data.records || []); setRecords(res.records || []);
setRecordPagination(prev => ({ ...prev, total: res.data.records?.length || 0, current: 1 })); setRecordPagination(prev => ({ ...prev, total: res.records?.length || 0, current: 1 }));
setSelectedRowKeys([]); setSelectedRowKeys([]);
setActiveTab('2'); setActiveTab('2');
} catch (error) { } catch (error) {
@@ -378,7 +378,7 @@ const InventoryTaskExecution = () => {
const fetchDeviceFields = async () => { const fetchDeviceFields = async () => {
try { try {
const res = await api.get('/deviceFields'); const res = await api.get('/deviceFields');
const sortedFields = res.data.sort((a, b) => a.order - b.order); const sortedFields = res.sort((a, b) => a.order - b.order);
setDeviceFields(sortedFields); setDeviceFields(sortedFields);
} catch (error) { } catch (error) {
console.error('获取字段配置失败:', error); console.error('获取字段配置失败:', error);
@@ -389,7 +389,7 @@ const InventoryTaskExecution = () => {
const fetchRooms = async () => { const fetchRooms = async () => {
try { try {
const res = await api.get('/rooms'); const res = await api.get('/rooms');
setRooms(res.data.rooms || res.data || []); setRooms(res.rooms || res || []);
} catch (error) { } catch (error) {
console.error('获取机房列表失败', error); console.error('获取机房列表失败', error);
} }
@@ -398,7 +398,7 @@ const InventoryTaskExecution = () => {
const fetchRacks = async () => { const fetchRacks = async () => {
try { try {
const res = await api.get('/racks', { params: { pageSize: 1000 } }); const res = await api.get('/racks', { params: { pageSize: 1000 } });
setRacks(res.data.racks || res.data || []); setRacks(res.racks || res || []);
} catch (error) { } catch (error) {
console.error('获取机柜列表失败', error); console.error('获取机柜列表失败', error);
} }
@@ -644,9 +644,9 @@ const InventoryTaskExecution = () => {
quickAddForm.resetFields(); quickAddForm.resetFields();
setScanResult({ setScanResult({
success: true, success: true,
message: `设备 "${res.data.pendingDevice.deviceName}" 已暂存,请前往「暂存设备」页面完善信息后同步到设备管理`, message: `设备 "${res.pendingDevice.deviceName}" 已暂存,请前往「暂存设备」页面完善信息后同步到设备管理`,
pendingDevice: res.data.pendingDevice, pendingDevice: res.pendingDevice,
sn: res.data.pendingDevice.serialNumber, sn: res.pendingDevice.serialNumber,
}); });
} catch (error) { } catch (error) {
message.error(error.response?.data?.error || '暂存设备失败'); message.error(error.response?.data?.error || '暂存设备失败');
+24 -26
View File
@@ -310,6 +310,7 @@ function RackManagement() {
const debouncedSearchKeyword = useDebounce(searchKeyword, 300); const debouncedSearchKeyword = useDebounce(searchKeyword, 300);
const [statusFilter, setStatusFilter] = useState('all'); const [statusFilter, setStatusFilter] = useState('all');
const [roomFilter, setRoomFilter] = useState('all'); const [roomFilter, setRoomFilter] = useState('all');
const [stats, setStats] = useState({ total: 0, active: 0, totalPower: 0, totalDevices: 0 });
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
@@ -324,18 +325,28 @@ function RackManagement() {
async (page = 1, pageSize = 10) => { async (page = 1, pageSize = 10) => {
try { try {
setLoading(true); setLoading(true);
const response = await api.get('/racks', { const [racksRes, statsRes] = await Promise.all([
params: { api.get('/racks', {
page, params: {
pageSize, page,
roomId: roomFilter, pageSize,
status: statusFilter, roomId: roomFilter,
keyword: debouncedSearchKeyword || undefined, status: statusFilter,
}, keyword: debouncedSearchKeyword || undefined,
}); },
const { racks: data, total } = response; }),
api.get('/racks/stats', {
params: {
roomId: roomFilter,
status: statusFilter,
keyword: debouncedSearchKeyword || undefined,
},
}),
]);
const { racks: data, total } = racksRes;
setRacks(data); setRacks(data);
setPagination(prev => ({ ...prev, current: page, pageSize, total })); setPagination(prev => ({ ...prev, current: page, pageSize, total }));
setStats(statsRes);
} catch (error) { } catch (error) {
message.error('获取机柜列表失败'); message.error('获取机柜列表失败');
console.error('获取机柜列表失败:', error); console.error('获取机柜列表失败:', error);
@@ -616,19 +627,6 @@ function RackManagement() {
); );
// 后端已过滤,直接使用 racks 数据 // 后端已过滤,直接使用 racks 数据
const filteredRacks = racks;
const stats = useMemo(
() => ({
total: racks.length,
active: racks.filter(r => r.status === 'active').length,
maintenance: racks.filter(r => r.status === 'maintenance').length,
totalPower: racks.reduce((sum, r) => sum + (r.currentPower || 0), 0),
totalDevices: racks.reduce((sum, r) => sum + (r.Devices?.length || 0), 0),
}),
[racks]
);
const tableColumns = [ const tableColumns = [
{ {
title: '机柜信息', title: '机柜信息',
@@ -967,7 +965,7 @@ function RackManagement() {
{viewMode === 'table' ? ( {viewMode === 'table' ? (
<Table <Table
columns={tableColumns} columns={tableColumns}
dataSource={filteredRacks} dataSource={racks}
rowKey="rackId" rowKey="rackId"
loading={loading} loading={loading}
rowSelection={rowSelection} rowSelection={rowSelection}
@@ -978,8 +976,8 @@ function RackManagement() {
/> />
) : ( ) : (
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
{filteredRacks.length > 0 ? ( {racks.length > 0 ? (
filteredRacks.map(rack => ( racks.map(rack => (
<Col xs={24} sm={12} lg={8} xl={6} key={rack.rackId}> <Col xs={24} sm={12} lg={8} xl={6} key={rack.rackId}>
<RackCard <RackCard
rack={rack} rack={rack}