feat(consumables): 添加耗材管理功能模块
添加耗材管理相关功能,包括: 1. 后端模型、路由和控制器 2. 前端页面和组件 3. 耗材分类管理 4. 耗材出入库记录 5. 耗材统计和日志功能 6. 集成到现有系统菜单
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../db');
|
||||
|
||||
const Consumable = sequelize.define('Consumable', {
|
||||
consumableId: {
|
||||
type: DataTypes.STRING,
|
||||
primaryKey: true,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
category: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
unit: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: '个'
|
||||
},
|
||||
currentStock: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0
|
||||
},
|
||||
minStock: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 10
|
||||
},
|
||||
maxStock: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 100
|
||||
},
|
||||
unitPrice: {
|
||||
type: DataTypes.DECIMAL(10, 2),
|
||||
allowNull: false,
|
||||
defaultValue: 0
|
||||
},
|
||||
supplier: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
location: {
|
||||
type: DataTypes.STRING,
|
||||
comment: '存放位置'
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'active'
|
||||
}
|
||||
}, {
|
||||
tableName: 'consumables',
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
module.exports = Consumable;
|
||||
@@ -0,0 +1,35 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../db');
|
||||
|
||||
const ConsumableCategory = sequelize.define('ConsumableCategory', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '分类名称'
|
||||
},
|
||||
description: {
|
||||
type: DataTypes.STRING,
|
||||
comment: '分类描述'
|
||||
},
|
||||
sortOrder: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: 0,
|
||||
comment: '排序顺序'
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'active',
|
||||
comment: '状态: active-启用, inactive-停用'
|
||||
}
|
||||
}, {
|
||||
tableName: 'consumable_categories',
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
module.exports = ConsumableCategory;
|
||||
@@ -0,0 +1,68 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../db');
|
||||
const Consumable = require('./Consumable');
|
||||
|
||||
const ConsumableLog = sequelize.define('ConsumableLog', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
consumableId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '耗材ID'
|
||||
},
|
||||
consumableName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
comment: '耗材名称'
|
||||
},
|
||||
operationType: {
|
||||
type: DataTypes.ENUM('in', 'out', 'create', 'update', 'delete', 'adjust', 'import'),
|
||||
allowNull: false,
|
||||
comment: '操作类型'
|
||||
},
|
||||
quantity: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: 0,
|
||||
comment: '变动数量(入库为正,出库为负)'
|
||||
},
|
||||
previousStock: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '操作前库存'
|
||||
},
|
||||
currentStock: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '操作后库存'
|
||||
},
|
||||
operator: {
|
||||
type: DataTypes.STRING,
|
||||
comment: '操作人'
|
||||
},
|
||||
reason: {
|
||||
type: DataTypes.STRING,
|
||||
comment: '操作原因'
|
||||
},
|
||||
notes: {
|
||||
type: DataTypes.TEXT,
|
||||
comment: '备注'
|
||||
},
|
||||
relatedId: {
|
||||
type: DataTypes.STRING,
|
||||
comment: '关联ID(如订单号、盘点ID等)'
|
||||
}
|
||||
}, {
|
||||
tableName: 'consumable_logs',
|
||||
timestamps: true,
|
||||
comment: '耗材操作日志表'
|
||||
});
|
||||
|
||||
ConsumableLog.belongsTo(Consumable, {
|
||||
foreignKey: 'consumableId',
|
||||
onDelete: 'CASCADE'
|
||||
});
|
||||
|
||||
module.exports = ConsumableLog;
|
||||
@@ -0,0 +1,64 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../db');
|
||||
const Consumable = require('./Consumable');
|
||||
|
||||
const ConsumableRecord = sequelize.define('ConsumableRecord', {
|
||||
recordId: {
|
||||
type: DataTypes.UUID,
|
||||
primaryKey: true,
|
||||
defaultValue: DataTypes.UUIDV4,
|
||||
allowNull: false,
|
||||
unique: true
|
||||
},
|
||||
consumableId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: Consumable,
|
||||
key: 'consumableId'
|
||||
}
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.ENUM('in', 'out'),
|
||||
allowNull: false
|
||||
},
|
||||
quantity: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
},
|
||||
previousStock: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
},
|
||||
currentStock: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false
|
||||
},
|
||||
operator: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
reason: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
recipient: {
|
||||
type: DataTypes.STRING
|
||||
},
|
||||
notes: {
|
||||
type: DataTypes.TEXT
|
||||
}
|
||||
}, {
|
||||
tableName: 'consumable_records',
|
||||
timestamps: true
|
||||
});
|
||||
|
||||
ConsumableRecord.belongsTo(Consumable, {
|
||||
foreignKey: 'consumableId',
|
||||
onDelete: 'CASCADE'
|
||||
});
|
||||
Consumable.hasMany(ConsumableRecord, {
|
||||
foreignKey: 'consumableId',
|
||||
onDelete: 'CASCADE'
|
||||
});
|
||||
|
||||
module.exports = ConsumableRecord;
|
||||
Reference in New Issue
Block a user