diff --git a/inventory-backend/app/api/v1/warehouse.py b/inventory-backend/app/api/v1/warehouse.py index fe48f52..27837cc 100644 --- a/inventory-backend/app/api/v1/warehouse.py +++ b/inventory-backend/app/api/v1/warehouse.py @@ -202,3 +202,157 @@ def delete_location(location_id): 'msg': str(e), 'data': None }), 500 + + +@warehouse_bp.route('/batch', methods=['DELETE']) +@jwt_required() +@audit_log( + module='库位管理', + action='批量删除' +) +def batch_delete_locations(): + """ + 批量删除库位 + """ + try: + ids = request.get_json() + if not ids or not isinstance(ids, list): + return jsonify({'code': 400, 'msg': '请提供要删除的库位ID列表', 'data': None}) + + deleted_count = 0 + deleted_names = [] + + for loc_id in ids: + location = SysWarehouseLocation.query.get(loc_id) + if not location: + continue + + # 在删除前提取属性 + deleted_names.append(location.name) + + # 递归删除 + def delete_recursive(loc): + children = SysWarehouseLocation.query.filter_by(parent_id=loc.id).all() + for child in children: + delete_recursive(child) + db.session.delete(loc) + + delete_recursive(location) + deleted_count += 1 + + db.session.commit() + + return jsonify({ + 'code': 200, + 'msg': f'删除成功,共删除 {deleted_count} 个库位', + 'data': {'deleted_count': deleted_count, 'deleted_names': deleted_names} + }) + except Exception as e: + db.session.rollback() + return jsonify({ + 'code': 500, + 'msg': str(e), + 'data': None + }), 500 + + +@warehouse_bp.route('/batch-generate', methods=['POST']) +@jwt_required() +@audit_log( + module='库位管理', + action='批量生成' +) +def batch_generate_locations(): + """ + 规则化批量新增库位 + """ + MAX_TOTAL = 3000 # 单次最多生成数量限制 + try: + data = request.get_json() + parent_id = data.get('parent_id') + rules = data.get('rules', []) + + if not rules: + return jsonify({'code': 400, 'msg': '请提供生成规则', 'data': None}) + + # 验证规则并计算总数 + total_count = 1 + for rule in rules: + start = rule.get('start', 1) + end = rule.get('end', 1) + total_count *= max(0, end - start + 1) + + if total_count > MAX_TOTAL: + return jsonify({'code': 400, 'msg': f'单次生成数量不能超过 {MAX_TOTAL} 个,当前计划生成 {total_count} 个', 'data': None}) + + # 初始化父级列表 + if parent_id: + parent = SysWarehouseLocation.query.get(parent_id) + if not parent: + return jsonify({'code': 404, 'msg': '父级库位不存在', 'data': None}) + parent_level = parent.level + parent_full_path = parent.full_path or '' + current_parents = [parent_id] + else: + parent_level = -1 # 顶级的话,第一层.level = 0 + parent_full_path = '' + current_parents = [None] + + # 逐层处理规则 + generated_ids = [] + + for rule in rules: + prefix = rule.get('prefix', '') + start = rule.get('start', 1) + end = rule.get('end', 1) + pad = rule.get('pad', 1) + + next_parents = [] + new_locations = [] + + for parent_id in current_parents: + # 计算该父级下的 level + if parent_id is None: + level = 0 + else: + p = SysWarehouseLocation.query.get(parent_id) + level = p.level + 1 if p else 0 + + for num in range(start, end + 1): + name = f"{prefix}{str(num).zfill(pad)}" + full_path = f"{parent_full_path}/{name}" if parent_full_path else name + + location = SysWarehouseLocation( + name=name, + parent_id=parent_id, + full_path=full_path, + level=level, + is_enabled=True + ) + db.session.add(location) + new_locations.append(location) + + # 立即刷新以获取 ID + db.session.flush() + generated_ids.extend([loc.id for loc in new_locations]) + + # 下一层的父级列表 + current_parents = [loc.id for loc in new_locations] + # 更新 full_path 的基准路径(为下一层准备) + if new_locations: + parent_full_path = new_locations[0].full_path + + db.session.commit() + + return jsonify({ + 'code': 200, + 'msg': f'生成成功,共生成 {len(generated_ids)} 个库位', + 'data': {'generated_count': len(generated_ids), 'generated_ids': generated_ids} + }) + except Exception as e: + db.session.rollback() + return jsonify({ + 'code': 500, + 'msg': str(e), + 'data': None + }), 500 diff --git a/inventory-web/src/api/common/warehouse.ts b/inventory-web/src/api/common/warehouse.ts index c2554e1..955b7da 100644 --- a/inventory-web/src/api/common/warehouse.ts +++ b/inventory-web/src/api/common/warehouse.ts @@ -33,3 +33,21 @@ export function deleteWarehouse(id: number) { method: 'delete' }) } + +// 批量删除库位 +export function batchDeleteWarehouse(ids: number[]) { + return request({ + url: '/v1/warehouse/batch', + method: 'delete', + data: ids + }) +} + +// 规则化批量生成库位 +export function batchGenerateWarehouse(data: { parent_id: number | null, rules: any[] }) { + return request({ + url: '/v1/warehouse/batch-generate', + method: 'post', + data + }) +} diff --git a/inventory-web/src/views/dashboard/index.vue b/inventory-web/src/views/dashboard/index.vue index 4367ca2..d8887ca 100644 --- a/inventory-web/src/views/dashboard/index.vue +++ b/inventory-web/src/views/dashboard/index.vue @@ -75,6 +75,12 @@ 新增顶级区域 + + 批量生成 + + + 批量删除 {{ selectedIds.length > 0 ? `(${selectedIds.length})` : '' }} + @@ -143,11 +151,54 @@ + + + + + + 清空选择 + + 层级规则(按顺序生成) + + + + + + + + + + + + + + + + + + + + + 添加层级 + + + 即将生成 {{ previewCount }} 个库位 + 请完善规则 + + + + 取消 + + 生成 {{ previewCount > 3000 ? '(超限)' : '' }} + + + +