feat: 添加请求参数验证中间件和设计令牌Hook
feat(validation): 为机房、机柜和设备路由添加Joi验证中间件 feat(hooks): 创建useDesignTokens Hook集中管理主题配置 feat(models): 为耗材模型添加乐观锁version字段和updatedAt索引 feat(3d): 增强3D场景和设备模型视觉效果与交互 refactor: 移除数据备份相关功能并优化代码结构 fix: 修复前端安全日志和密码加密工具 chore: 更新依赖并添加axios和joi库 docs: 更新注释和文档说明 style: 改进代码格式和命名一致性
This commit is contained in:
+256
-162
@@ -250,187 +250,281 @@ router.get('/inout/records', async (req, res) => {
|
||||
});
|
||||
|
||||
router.post('/quick-inout', async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
const { consumableId, type, quantity, operator, reason, notes } = req.body;
|
||||
|
||||
const consumable = await Consumable.findByPk(consumableId, { transaction });
|
||||
if (!consumable) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json({ error: '耗材不存在' });
|
||||
}
|
||||
|
||||
const previousStock = parseFloat(consumable.currentStock);
|
||||
let newStock;
|
||||
|
||||
if (type === 'in') {
|
||||
newStock = previousStock + parseFloat(quantity);
|
||||
} else if (type === 'out') {
|
||||
newStock = previousStock - parseFloat(quantity);
|
||||
if (newStock < 0) {
|
||||
const MAX_RETRIES = 3;
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < MAX_RETRIES) {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
const { consumableId, type, quantity, operator, reason, notes } = req.body;
|
||||
|
||||
const consumable = await Consumable.findByPk(consumableId, { transaction });
|
||||
if (!consumable) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '库存不足' });
|
||||
return res.status(404).json({ error: '耗材不存在' });
|
||||
}
|
||||
} else {
|
||||
|
||||
const previousStock = parseFloat(consumable.currentStock);
|
||||
let newStock;
|
||||
|
||||
if (type === 'in') {
|
||||
newStock = previousStock + parseFloat(quantity);
|
||||
} else if (type === 'out') {
|
||||
newStock = previousStock - parseFloat(quantity);
|
||||
if (newStock < 0) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '库存不足' });
|
||||
}
|
||||
} else {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '操作类型无效' });
|
||||
}
|
||||
|
||||
const [affectedRows] = await Consumable.update(
|
||||
{
|
||||
currentStock: newStock,
|
||||
version: sequelize.literal('version + 1')
|
||||
},
|
||||
{
|
||||
where: {
|
||||
consumableId,
|
||||
version: consumable.version
|
||||
},
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
if (affectedRows === 0) {
|
||||
await transaction.rollback();
|
||||
attempt++;
|
||||
if (attempt >= MAX_RETRIES) {
|
||||
return res.status(409).json({ error: '并发冲突,请稍后重试' });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const record = await ConsumableRecord.create({
|
||||
consumableId,
|
||||
type,
|
||||
quantity,
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason,
|
||||
notes
|
||||
}, { transaction });
|
||||
|
||||
await ConsumableLog.create({
|
||||
consumableId,
|
||||
consumableName: consumable.name,
|
||||
operationType: type,
|
||||
quantity: type === 'in' ? parseFloat(quantity) : -parseFloat(quantity),
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason,
|
||||
notes
|
||||
}, { transaction });
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json({
|
||||
message: '操作成功',
|
||||
record,
|
||||
consumable: await Consumable.findByPk(consumableId)
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '操作类型无效' });
|
||||
if (attempt >= MAX_RETRIES - 1) {
|
||||
return res.status(500).json({ error: error.message });
|
||||
}
|
||||
attempt++;
|
||||
}
|
||||
|
||||
await consumable.update({ currentStock: newStock }, { transaction });
|
||||
|
||||
const record = await ConsumableRecord.create({
|
||||
consumableId,
|
||||
type,
|
||||
quantity,
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason,
|
||||
notes
|
||||
}, { transaction });
|
||||
|
||||
await ConsumableLog.create({
|
||||
consumableId,
|
||||
consumableName: consumable.name,
|
||||
operationType: type,
|
||||
quantity: type === 'in' ? parseFloat(quantity) : -parseFloat(quantity),
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason,
|
||||
notes
|
||||
}, { transaction });
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json({
|
||||
message: '操作成功',
|
||||
record,
|
||||
consumable: await consumable.reload()
|
||||
});
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/inout', async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
const { consumableId, type, quantity, operator, reason, recipient, notes } = req.body;
|
||||
|
||||
const consumable = await Consumable.findByPk(consumableId, { transaction });
|
||||
if (!consumable) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json({ error: '耗材不存在' });
|
||||
}
|
||||
|
||||
const previousStock = parseFloat(consumable.currentStock);
|
||||
let newStock;
|
||||
|
||||
if (type === 'in') {
|
||||
newStock = previousStock + parseFloat(quantity);
|
||||
} else {
|
||||
newStock = previousStock - parseFloat(quantity);
|
||||
if (newStock < 0) {
|
||||
const MAX_RETRIES = 3;
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < MAX_RETRIES) {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
const { consumableId, type, quantity, operator, reason, recipient, notes } = req.body;
|
||||
|
||||
const consumable = await Consumable.findByPk(consumableId, { transaction });
|
||||
if (!consumable) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '库存不足' });
|
||||
return res.status(404).json({ error: '耗材不存在' });
|
||||
}
|
||||
|
||||
const previousStock = parseFloat(consumable.currentStock);
|
||||
let newStock;
|
||||
|
||||
if (type === 'in') {
|
||||
newStock = previousStock + parseFloat(quantity);
|
||||
} else {
|
||||
newStock = previousStock - parseFloat(quantity);
|
||||
if (newStock < 0) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '库存不足' });
|
||||
}
|
||||
}
|
||||
|
||||
const [affectedRows] = await Consumable.update(
|
||||
{
|
||||
currentStock: newStock,
|
||||
version: sequelize.literal('version + 1')
|
||||
},
|
||||
{
|
||||
where: {
|
||||
consumableId,
|
||||
version: consumable.version
|
||||
},
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
if (affectedRows === 0) {
|
||||
await transaction.rollback();
|
||||
attempt++;
|
||||
if (attempt >= MAX_RETRIES) {
|
||||
return res.status(409).json({ error: '并发冲突,请稍后重试' });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const record = await ConsumableRecord.create({
|
||||
consumableId,
|
||||
type,
|
||||
quantity,
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason,
|
||||
recipient,
|
||||
notes
|
||||
}, { transaction });
|
||||
|
||||
await ConsumableLog.create({
|
||||
consumableId,
|
||||
consumableName: consumable.name,
|
||||
operationType: type,
|
||||
quantity: type === 'in' ? parseFloat(quantity) : -parseFloat(quantity),
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason,
|
||||
notes
|
||||
}, { transaction });
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json({
|
||||
message: '操作成功',
|
||||
record,
|
||||
consumable: await Consumable.findByPk(consumableId)
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
if (attempt >= MAX_RETRIES - 1) {
|
||||
return res.status(500).json({ error: error.message });
|
||||
}
|
||||
attempt++;
|
||||
}
|
||||
|
||||
await consumable.update({ currentStock: newStock }, { transaction });
|
||||
|
||||
const record = await ConsumableRecord.create({
|
||||
consumableId,
|
||||
type,
|
||||
quantity,
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason,
|
||||
recipient,
|
||||
notes
|
||||
}, { transaction });
|
||||
|
||||
await ConsumableLog.create({
|
||||
consumableId,
|
||||
consumableName: consumable.name,
|
||||
operationType: type,
|
||||
quantity: type === 'in' ? parseFloat(quantity) : -parseFloat(quantity),
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason,
|
||||
notes
|
||||
}, { transaction });
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json({
|
||||
message: '操作成功',
|
||||
record,
|
||||
consumable: await consumable.reload()
|
||||
});
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/adjust', async (req, res) => {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
const { consumableId, adjustType, quantity, operator, reason, notes } = req.body;
|
||||
|
||||
const consumable = await Consumable.findByPk(consumableId, { transaction });
|
||||
if (!consumable) {
|
||||
await transaction.rollback();
|
||||
return res.status(404).json({ error: '耗材不存在' });
|
||||
}
|
||||
|
||||
const previousStock = parseFloat(consumable.currentStock);
|
||||
let newStock;
|
||||
|
||||
if (adjustType === 'add') {
|
||||
newStock = previousStock + parseFloat(quantity);
|
||||
} else if (adjustType === 'subtract') {
|
||||
newStock = previousStock - parseFloat(quantity);
|
||||
if (newStock < 0) {
|
||||
const MAX_RETRIES = 3;
|
||||
let attempt = 0;
|
||||
|
||||
while (attempt < MAX_RETRIES) {
|
||||
const transaction = await sequelize.transaction();
|
||||
try {
|
||||
const { consumableId, adjustType, quantity, operator, reason, notes } = req.body;
|
||||
|
||||
const consumable = await Consumable.findByPk(consumableId, { transaction });
|
||||
if (!consumable) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '调整后库存不能为负' });
|
||||
return res.status(404).json({ error: '耗材不存在' });
|
||||
}
|
||||
} else if (adjustType === 'set') {
|
||||
newStock = parseFloat(quantity);
|
||||
} else {
|
||||
|
||||
const previousStock = parseFloat(consumable.currentStock);
|
||||
let newStock;
|
||||
|
||||
if (adjustType === 'add') {
|
||||
newStock = previousStock + parseFloat(quantity);
|
||||
} else if (adjustType === 'subtract') {
|
||||
newStock = previousStock - parseFloat(quantity);
|
||||
if (newStock < 0) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '调整后库存不能为负' });
|
||||
}
|
||||
} else if (adjustType === 'set') {
|
||||
newStock = parseFloat(quantity);
|
||||
if (newStock < 0) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '库存不能设置为负数' });
|
||||
}
|
||||
} else {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '调整类型无效' });
|
||||
}
|
||||
|
||||
const [affectedRows] = await Consumable.update(
|
||||
{
|
||||
currentStock: newStock,
|
||||
version: sequelize.literal('version + 1')
|
||||
},
|
||||
{
|
||||
where: {
|
||||
consumableId,
|
||||
version: consumable.version
|
||||
},
|
||||
transaction
|
||||
}
|
||||
);
|
||||
|
||||
if (affectedRows === 0) {
|
||||
await transaction.rollback();
|
||||
attempt++;
|
||||
if (attempt >= MAX_RETRIES) {
|
||||
return res.status(409).json({ error: '并发冲突,请稍后重试' });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const changeQuantity = newStock - previousStock;
|
||||
|
||||
await ConsumableLog.create({
|
||||
consumableId,
|
||||
consumableName: consumable.name,
|
||||
operationType: 'adjust',
|
||||
quantity: changeQuantity,
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason: reason || (adjustType === 'set' ? '库存调整为 ' + newStock : reason),
|
||||
notes: adjustType === 'set' ? `库存从 ${previousStock} 调整为 ${newStock}` : notes
|
||||
}, { transaction });
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json({
|
||||
message: '调整成功',
|
||||
consumable: await Consumable.findByPk(consumableId)
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
return res.status(400).json({ error: '调整类型无效' });
|
||||
if (attempt >= MAX_RETRIES - 1) {
|
||||
return res.status(500).json({ error: error.message });
|
||||
}
|
||||
attempt++;
|
||||
}
|
||||
|
||||
await consumable.update({ currentStock: newStock }, { transaction });
|
||||
|
||||
const changeQuantity = newStock - previousStock;
|
||||
|
||||
await ConsumableLog.create({
|
||||
consumableId,
|
||||
consumableName: consumable.name,
|
||||
operationType: 'adjust',
|
||||
quantity: changeQuantity,
|
||||
previousStock,
|
||||
currentStock: newStock,
|
||||
operator,
|
||||
reason: reason || (adjustType === 'set' ? '库存调整为 ' + newStock : reason),
|
||||
notes: adjustType === 'set' ? `库存从 ${previousStock} 调整为 ${newStock}` : notes
|
||||
}, { transaction });
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
res.json({
|
||||
message: '调整成功',
|
||||
consumable: await consumable.reload()
|
||||
});
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+15
-18
@@ -14,9 +14,18 @@ const DeviceField = require('../models/DeviceField');
|
||||
const Ticket = require('../models/Ticket');
|
||||
const DevicePort = require('../models/DevicePort'); // Import DevicePort
|
||||
const Cable = require('../models/Cable'); // Import Cable
|
||||
const { validateBody, validateQuery } = require('../middleware/validation');
|
||||
const {
|
||||
createDeviceSchema,
|
||||
updateDeviceSchema,
|
||||
batchDeviceIdsSchema,
|
||||
batchStatusSchema,
|
||||
batchMoveSchema,
|
||||
queryDeviceSchema
|
||||
} = require('../validation/deviceSchema');
|
||||
|
||||
// 获取所有设备(支持搜索和筛选)
|
||||
router.get('/', async (req, res) => {
|
||||
router.get('/', validateQuery(queryDeviceSchema), async (req, res) => {
|
||||
try {
|
||||
const { keyword, status, type, rackId, page = 1, pageSize = 10 } = req.query;
|
||||
const offset = (page - 1) * pageSize;
|
||||
@@ -80,7 +89,7 @@ router.get('/', async (req, res) => {
|
||||
});
|
||||
|
||||
// 创建设备
|
||||
router.post('/', async (req, res) => {
|
||||
router.post('/', validateBody(createDeviceSchema), async (req, res) => {
|
||||
try {
|
||||
const device = await Device.create(req.body);
|
||||
|
||||
@@ -602,7 +611,7 @@ router.post('/import', async (req, res) => {
|
||||
});
|
||||
|
||||
// 批量上线设备
|
||||
router.put('/batch-online', async (req, res) => {
|
||||
router.put('/batch-online', validateBody(batchDeviceIdsSchema), async (req, res) => {
|
||||
try {
|
||||
const { deviceIds } = req.body;
|
||||
|
||||
@@ -626,7 +635,7 @@ router.put('/batch-online', async (req, res) => {
|
||||
});
|
||||
|
||||
// 批量下线设备
|
||||
router.put('/batch-offline', async (req, res) => {
|
||||
router.put('/batch-offline', validateBody(batchDeviceIdsSchema), async (req, res) => {
|
||||
try {
|
||||
const { deviceIds } = req.body;
|
||||
|
||||
@@ -654,8 +663,6 @@ router.put('/batch-status', async (req, res) => {
|
||||
try {
|
||||
const { deviceIds, status } = req.body;
|
||||
|
||||
console.log('批量状态变更请求:', { deviceIds, status });
|
||||
|
||||
if (!deviceIds || !Array.isArray(deviceIds) || deviceIds.length === 0) {
|
||||
return res.status(400).json({ error: '请提供有效的设备ID列表' });
|
||||
}
|
||||
@@ -666,15 +673,11 @@ router.put('/batch-status', async (req, res) => {
|
||||
attributes: ['deviceId']
|
||||
});
|
||||
|
||||
console.log('数据库中找到的设备:', existingDevices.map(d => d.deviceId));
|
||||
console.log('请求的设备ID:', deviceIds);
|
||||
|
||||
// 检查是否有不存在的设备
|
||||
const existingIds = existingDevices.map(d => d.deviceId);
|
||||
const missingIds = deviceIds.filter(id => !existingIds.includes(id));
|
||||
|
||||
if (missingIds.length > 0) {
|
||||
console.log('不存在的设备ID:', missingIds);
|
||||
return res.status(404).json({ error: `设备不存在: ${missingIds.join(', ')}` });
|
||||
}
|
||||
|
||||
@@ -780,7 +783,7 @@ router.get('/:deviceId', async (req, res) => {
|
||||
});
|
||||
|
||||
// 更新设备
|
||||
router.put('/:deviceId', async (req, res) => {
|
||||
router.put('/:deviceId', validateBody(updateDeviceSchema), async (req, res) => {
|
||||
try {
|
||||
// 获取旧设备信息以更新功率
|
||||
const oldDevice = await Device.findByPk(req.params.deviceId);
|
||||
@@ -843,7 +846,7 @@ router.put('/batch-offline', async (req, res) => {
|
||||
});
|
||||
|
||||
// 批量删除设备
|
||||
router.delete('/batch-delete', async (req, res) => {
|
||||
router.delete('/batch-delete', validateBody(batchDeviceIdsSchema), async (req, res) => {
|
||||
const t = await sequelize.transaction();
|
||||
try {
|
||||
const { deviceIds } = req.body;
|
||||
@@ -1045,8 +1048,6 @@ router.put('/batch-status', async (req, res) => {
|
||||
try {
|
||||
const { deviceIds, status } = req.body;
|
||||
|
||||
console.log('批量状态变更请求:', { deviceIds, status });
|
||||
|
||||
if (!deviceIds || !Array.isArray(deviceIds) || deviceIds.length === 0) {
|
||||
return res.status(400).json({ error: '请提供有效的设备ID列表' });
|
||||
}
|
||||
@@ -1057,15 +1058,11 @@ router.put('/batch-status', async (req, res) => {
|
||||
attributes: ['deviceId']
|
||||
});
|
||||
|
||||
console.log('数据库中找到的设备:', existingDevices.map(d => d.deviceId));
|
||||
console.log('请求的设备ID:', deviceIds);
|
||||
|
||||
// 检查是否有不存在的设备
|
||||
const existingIds = existingDevices.map(d => d.deviceId);
|
||||
const missingIds = deviceIds.filter(id => !existingIds.includes(id));
|
||||
|
||||
if (missingIds.length > 0) {
|
||||
console.log('不存在的设备ID:', missingIds);
|
||||
return res.status(404).json({ error: `设备不存在: ${missingIds.join(', ')}` });
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ const Room = require('../models/Room');
|
||||
const XLSX = require('xlsx');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { validateBody, validateQuery } = require('../middleware/validation');
|
||||
const { createRackSchema, updateRackSchema, queryRackSchema } = require('../validation/rackSchema');
|
||||
|
||||
// 获取所有机柜
|
||||
router.get('/', async (req, res) => {
|
||||
@@ -57,7 +59,7 @@ router.get('/:rackId', async (req, res) => {
|
||||
});
|
||||
|
||||
// 创建机柜
|
||||
router.post('/', async (req, res) => {
|
||||
router.post('/', validateBody(createRackSchema), async (req, res) => {
|
||||
try {
|
||||
const rack = await Rack.create(req.body);
|
||||
res.status(201).json(rack);
|
||||
@@ -67,7 +69,7 @@ router.post('/', async (req, res) => {
|
||||
});
|
||||
|
||||
// 更新机柜
|
||||
router.put('/:rackId', async (req, res) => {
|
||||
router.put('/:rackId', validateBody(updateRackSchema), async (req, res) => {
|
||||
try {
|
||||
const [updated] = await Rack.update(req.body, {
|
||||
where: { rackId: req.params.rackId }
|
||||
|
||||
@@ -2,6 +2,8 @@ const express = require('express');
|
||||
const router = express.Router();
|
||||
const Room = require('../models/Room');
|
||||
const Rack = require('../models/Rack');
|
||||
const { validateBody } = require('../middleware/validation');
|
||||
const { createRoomSchema, updateRoomSchema } = require('../validation/roomSchema');
|
||||
|
||||
// 获取所有机房
|
||||
router.get('/', async (req, res) => {
|
||||
@@ -41,7 +43,7 @@ router.post('/', async (req, res) => {
|
||||
});
|
||||
|
||||
// 更新机房
|
||||
router.put('/:roomId', async (req, res) => {
|
||||
router.put('/:roomId', validateBody(updateRoomSchema), async (req, res) => {
|
||||
try {
|
||||
const [updated] = await Room.update(req.body, {
|
||||
where: { roomId: req.params.roomId }
|
||||
|
||||
@@ -20,19 +20,11 @@ const initDefaultSettings = async () => {
|
||||
// 外观设置
|
||||
{ settingKey: 'primary_color', settingValue: JSON.stringify('#667eea'), settingType: 'string', category: 'appearance', description: '主题主色调', isEditable: true },
|
||||
{ settingKey: 'secondary_color', settingValue: JSON.stringify('#764ba2'), settingType: 'string', category: 'appearance', description: '主题辅助色调', isEditable: true },
|
||||
{ settingKey: 'compact_mode', settingValue: JSON.stringify(false), type: 'boolean', category: 'appearance', description: '紧凑模式', isEditable: true },
|
||||
{ settingKey: 'compact_mode', settingValue: JSON.stringify(false), settingType: 'boolean', category: 'appearance', description: '紧凑模式', isEditable: true },
|
||||
{ settingKey: 'sidebar_collapsed', settingValue: JSON.stringify(false), settingType: 'boolean', category: 'appearance', description: '侧边栏默认折叠', isEditable: true },
|
||||
{ settingKey: 'table_row_height', settingValue: JSON.stringify('default'), settingType: 'string', category: 'appearance', description: '表格行高: small/default/middle/large', isEditable: true },
|
||||
{ settingKey: 'animation_enabled', settingValue: JSON.stringify(true), settingType: 'boolean', category: 'appearance', description: '启用动画效果', isEditable: true },
|
||||
|
||||
// 数据备份设置
|
||||
{ settingKey: 'auto_backup_enabled', settingValue: JSON.stringify(false), settingType: 'boolean', category: 'backup', description: '启用自动备份', isEditable: true },
|
||||
{ settingKey: 'backup_interval', settingValue: JSON.stringify(24), settingType: 'number', category: 'backup', description: '备份间隔(小时)', isEditable: true },
|
||||
{ settingKey: 'backup_retention', settingValue: JSON.stringify(7), settingType: 'number', category: 'backup', description: '备份保留天数', isEditable: true },
|
||||
{ settingKey: 'backup_path', settingValue: JSON.stringify('./backups'), settingType: 'string', category: 'backup', description: '备份存储路径', isEditable: true },
|
||||
{ settingKey: 'last_backup_time', settingValue: JSON.stringify(null), settingType: 'string', category: 'backup', description: '上次备份时间', isEditable: false },
|
||||
{ settingKey: 'backup_count', settingValue: JSON.stringify(0), settingType: 'number', category: 'backup', description: '备份文件数量', isEditable: false },
|
||||
|
||||
// 关于页面
|
||||
{ settingKey: 'app_version', settingValue: JSON.stringify('1.0.0'), settingType: 'string', category: 'about', description: '应用版本', isEditable: false },
|
||||
{ settingKey: 'company_name', settingValue: JSON.stringify(''), settingType: 'string', category: 'about', description: '公司/组织名称', isEditable: true },
|
||||
@@ -241,10 +233,6 @@ router.post('/reset/:key', async (req, res) => {
|
||||
sidebar_collapsed: false,
|
||||
table_row_height: 'default',
|
||||
animation_enabled: true,
|
||||
auto_backup_enabled: false,
|
||||
backup_interval: 24,
|
||||
backup_retention: 7,
|
||||
backup_path: './backups',
|
||||
company_name: '',
|
||||
contact_email: '',
|
||||
contact_phone: '',
|
||||
|
||||
Reference in New Issue
Block a user