PSI数据导入导出与Excel模板配置
数据导入导出概述
数据导入导出是进销存软件的核心功能之一,主要用于:初始化数据导入、批量修改数据、与其他系统数据对接、报表导出备份等场景。
支持的数据类型
| 数据类型 | 导入 | 导出 | 文件格式 |
|---|---|---|---|
| 商品档案 | 支持 | 支持 | Excel/CSV |
| 客户档案 | 支持 | 支持 | Excel/CSV |
| 供应商档案 | 支持 | 支持 | Excel |
| 库存数据 | 支持 | 支持 | Excel |
| 单据数据 | 部分支持 | 支持 | Excel |
商品档案导入示例
// 导入服务
class ImportService {
constructor(ctx) {
this.ctx = ctx;
}
// 解析 Excel 文件
async parseExcel(filePath) {
const XLSX = require('xlsx');
const workbook = XLSX.readFile(filePath);
const sheetName = workbook.SheetNames[0];
const data = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName]);
return data;
}
// 验证商品数据
validateProduct(row, rowNum) {
const errors = [];
// 必填字段校验
if (!row['商品编码']) {
errors.push(`第${rowNum}行:商品编码不能为空`);
}
if (!row['商品名称']) {
errors.push(`第${rowNum}行:商品名称不能为空`);
}
// 格式校验
if (row['商品编码'] && !/^[A-Za-z0-9_-]+$/.test(row['商品编码'])) {
errors.push(`第${rowNum}行:商品编码格式不正确`);
}
if (row['零售价'] && isNaN(parseFloat(row['零售价']))) {
errors.push(`第${rowNum}行:零售价必须是数字`);
}
return errors;
}
// 导入商品档案
async importProducts(filePath) {
const rows = await this.parseExcel(filePath);
const success = [];
const errors = [];
const existed = [];
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
const rowNum = i + 2; // Excel 行号(从2开始,1是表头)
// 数据验证
const validationErrors = this.validateProduct(row, rowNum);
if (validationErrors.length > 0) {
errors.push(...validationErrors);
continue;
}
// 检查是否已存在
const existProduct = await this.ctx.model.Product.findOne({
productCode: row['商品编码']
});
if (existProduct) {
existed.push(`第${rowNum}行:商品编码 ${row['商品编码']} 已存在`);
continue;
}
// 创建商品
const product = await this.ctx.model.Product.create({
productCode: row['商品编码'],
productName: row['商品名称'],
category: row['商品分类'],
unit: row['单位'],
costPrice: parseFloat(row['成本价'] || 0),
salePrice: parseFloat(row['零售价'] || 0),
barCode: row['条码'],
status: 'normal',
createTime: new Date()
});
success.push(product);
}
return {
total: rows.length,
success: success.length,
existed: existed.length,
errors: [...errors, ...existed]
};
}
}
Excel 导出实现
// 导出服务
class ExportService {
constructor(ctx) {
this.ctx = ctx;
}
// 导出商品档案
async exportProducts(filters = {}) {
const XLSX = require('xlsx');
// 查询数据
const products = await this.ctx.model.Product.find(filters)
.populate('category', 'categoryName')
.lean();
// 转换数据格式
const data = products.map(p => ({
'商品编码': p.productCode,
'商品名称': p.productName,
'商品分类': p.category?.categoryName || '',
'单位': p.unit,
'成本价': p.costPrice,
'零售价': p.salePrice,
'批发价': p.wholesalePrice,
'条码': p.barCode,
'商品规格': p.spec,
'状态': p.status === 'normal' ? '正常' : '停用',
'创建日期': this.formatDate(p.createTime)
}));
// 创建工作表
const worksheet = XLSX.utils.json_to_sheet(data);
// 设置列宽
worksheet['!cols'] = [
{ wch: 15 }, // 商品编码
{ wch: 30 }, // 商品名称
{ wch: 15 }, // 分类
{ wch: 8 }, // 单位
{ wch: 12 }, // 成本价
{ wch: 12 }, // 零售价
{ wch: 12 }, // 批发价
{ wch: 20 }, // 条码
{ wch: 20 }, // 规格
{ wch: 8 }, // 状态
{ wch: 15 } // 创建日期
];
// 创建工作簿
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '商品档案');
// 生成文件
const exportDir = path.join(__dirname, 'exports');
if (!fs.existsSync(exportDir)) {
fs.mkdirSync(exportDir, { recursive: true });
}
const filename = `商品档案_${this.formatDate(new Date(), 'YYYYMMDDHHmmss')}.xlsx`;
const filePath = path.join(exportDir, filename);
XLSX.writeFile(workbook, filePath);
return {
filename,
filePath,
recordCount: data.length
};
}
// 导出库存数据
async exportInventory(warehouseId) {
const XLSX = require('xlsx');
const query = {};
if (warehouseId) {
query.warehouseId = warehouseId;
}
const inventory = await this.ctx.model.Inventory.find(query)
.populate('productId', 'productCode productName unit')
.populate('warehouseId', 'warehouseName')
.lean();
const data = inventory.map(inv => ({
'仓库': inv.warehouseId?.warehouseName || '',
'商品编码': inv.productId?.productCode || '',
'商品名称': inv.productId?.productName || '',
'单位': inv.productId?.unit || '',
'库存数量': inv.quantity,
'锁定数量': inv.lockedQuantity,
'可用数量': inv.quantity - inv.lockedQuantity,
'成本金额': (inv.quantity * inv.costPrice).toFixed(2),
'零售金额': (inv.quantity * inv.salePrice).toFixed(2)
}));
// 计算合计行
const totalQuantity = data.reduce((sum, item) => sum + item['库存数量'], 0);
data.push({
'仓库': '合计',
'商品编码': '',
'商品名称': '',
'单位': '',
'库存数量': totalQuantity,
'锁定数量': '',
'可用数量': '',
'成本金额': data.reduce((sum, item) => sum + parseFloat(item['成本金额'] || 0), 0).toFixed(2),
'零售金额': data.reduce((sum, item) => sum + parseFloat(item['零售金额'] || 0), 0).toFixed(2)
});
const worksheet = XLSX.utils.json_to_sheet(data);
worksheet['!cols'] = [
{ wch: 15 }, { wch: 15 }, { wch: 30 }, { wch: 8 },
{ wch: 12 }, { wch: 12 }, { wch: 12 }, { wch: 15 }, { wch: 15 }
];
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, '库存数据');
const filename = `库存数据_${this.formatDate(new Date(), 'YYYYMMDDHHmmss')}.xlsx`;
const filePath = path.join(__dirname, 'exports', filename);
XLSX.writeFile(workbook, filePath);
return { filename, filePath, recordCount: data.length };
}
formatDate(date, format = 'YYYY-MM-DD') {
if (!date) return '';
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const hours = String(d.getHours()).padStart(2, '0');
const minutes = String(d.getMinutes()).padStart(2, '0');
const seconds = String(d.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', year)
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}
}
导入模板配置
系统默认提供标准导入模板,可根据需要自定义字段映射:
商品导入模板字段说明
| 字段名称 | 是否必填 | 说明 | 示例 |
|---|---|---|---|
| 商品编码 | 是 | 唯一标识 | P001 |
| 商品名称 | 是 | 商品全称 | iPhone 15 Pro |
| 商品分类 | 否 | 分类名称 | 电子产品 |
| 单位 | 否 | 默认:个 | 台/箱/个 |
| 成本价 | 否 | 数字 | 5000 |
| 零售价 | 否 | 数字 | 5999 |
| 条码 | 否 | 商品条码 | 6901234567890 |
导入导出使用流程
导入操作步骤
- 在系统中下载标准导入模板
- 按照模板格式填写数据
- 进入数据导入页面,选择导入类型
- 上传 Excel 文件
- 系统自动校验数据,显示预览
- 确认无误后执行导入
- 查看导入结果报告
导出操作步骤
- 进入数据导出页面
- 选择要导出的数据类型
- 设置筛选条件(如日期范围、仓库等)
- 点击导出按钮
- 系统生成 Excel 文件并下载
注意事项
- 导入文件大小限制:10MB
- 单次导入记录数上限:10000条
- 导出超时时间:60秒
- 建议使用 Excel 格式导出,避免 CSV 编码问题
- 导入前请先备份现有数据
总结
数据导入导出功能可以大大提高日常工作效率,特别是在初始化阶段和批量数据处理时。注意定期备份数据,导入操作建议先在小数据集上测试验证。