diff --git a/inventory-backend/app/api/v1/warehouse.py b/inventory-backend/app/api/v1/warehouse.py index f002e2e..6b7a351 100644 --- a/inventory-backend/app/api/v1/warehouse.py +++ b/inventory-backend/app/api/v1/warehouse.py @@ -54,6 +54,41 @@ def get_tree(): }), 500 +@warehouse_bp.route('/children', methods=['GET']) +def get_children(): + """ + 懒加载:获取指定库位的直接子节点(parent_id 省略/为空 = 顶层)。 + 每个节点附带 has_children 标记,前端据此渲染「进入下级」而非点进去才知道。 + 与 /tree 行为一致(不额外过滤 is_enabled、按 name 升序)。 + """ + try: + parent_id = request.args.get('parent_id', type=int) + if parent_id is None: + nodes = SysWarehouseLocation.query.filter( + SysWarehouseLocation.parent_id.is_(None) + ).order_by(SysWarehouseLocation.name.asc()).all() + else: + nodes = SysWarehouseLocation.query.filter( + SysWarehouseLocation.parent_id == parent_id + ).order_by(SysWarehouseLocation.name.asc()).all() + + # 一次查询所有"有子节点"的 parent_id,用于 has_children 判断(避免 N+1) + parent_with_children = set( + cid for (cid,) in db.session.query(SysWarehouseLocation.parent_id) + .filter(SysWarehouseLocation.parent_id.isnot(None)).distinct().all() + ) + + data = [] + for node in nodes: + d = node.to_dict() + d['has_children'] = node.id in parent_with_children + data.append(d) + + return jsonify({'code': 200, 'msg': 'success', 'data': data}) + except Exception as e: + return jsonify({'code': 500, 'msg': str(e), 'data': None}), 500 + + @warehouse_bp.route('', methods=['POST']) @jwt_required() @audit_log( diff --git a/inventory-web/src/api/common/warehouse.ts b/inventory-web/src/api/common/warehouse.ts index 955b7da..c3685bc 100644 --- a/inventory-web/src/api/common/warehouse.ts +++ b/inventory-web/src/api/common/warehouse.ts @@ -8,6 +8,15 @@ export function getWarehouseTree() { }) } +// 懒加载:获取指定库位的直接子节点(parentId 省略/为空 = 顶层) +export function getWarehouseChildren(parentId?: number | null) { + return request({ + url: '/v1/warehouse/children', + method: 'get', + params: parentId != null ? { parent_id: parentId } : {} + }) +} + // 创建库位 export function createWarehouse(data: any) { return request({ diff --git a/inventory-web/src/components/WarehouseSelector.vue b/inventory-web/src/components/WarehouseSelector.vue index 4e3f9f2..ced5383 100644 --- a/inventory-web/src/components/WarehouseSelector.vue +++ b/inventory-web/src/components/WarehouseSelector.vue @@ -69,18 +69,18 @@
{{ item.name }} - +
- + 末级 diff --git a/inventory-web/src/views/stock/inbound/product.vue b/inventory-web/src/views/stock/inbound/product.vue index 611106c..d8c72ea 100644 --- a/inventory-web/src/views/stock/inbound/product.vue +++ b/inventory-web/src/views/stock/inbound/product.vue @@ -354,10 +354,7 @@ - + @@ -620,7 +617,6 @@ import WarehouseSelector from '@/components/WarehouseSelector.vue' import SmartScannerDialog from '@/components/SmartScannerDialog.vue' import TrackScanDialog from '@/components/TrackScanDialog.vue' import { getLabelPreview, executePrint } from '@/api/common/print' -import { getWarehouseTree } from '@/api/common/warehouse' import { usePasteUpload } from '@/hooks/usePasteUpload' import { useUserStore } from '@/stores/user' @@ -759,9 +755,6 @@ const scannerDialogVisible = ref(false) // 顶部扫码入库面板 (Track 身份证自动填充物料+序列号+订单号) const trackScanVisible = ref(false) -// 库位级联选择器数据 -const warehouseOptions = ref([]) - // ================= 第一步:声明基础数据 ================= // [核心优化] 所有列定义 @@ -1208,18 +1201,6 @@ const buildCategoryTree = (categories: string[]) => { return root; }; -// 加载库位树数据 -const loadWarehouseTree = async () => { - try { - const res = await getWarehouseTree() - if (res.code === 200) { - warehouseOptions.value = res.data || [] - } - } catch (e) { - console.error('加载库位树失败', e) - } -} - const resetQuery = () => { queryParams.keyword = '' queryParams.searchField = 'all' @@ -1532,7 +1513,6 @@ onMounted(() => { initColumnPermissions() fetchData() fetchOptions() - loadWarehouseTree() }) // 成本计算监听 diff --git a/inventory-web/src/views/stock/inbound/semi.vue b/inventory-web/src/views/stock/inbound/semi.vue index 4bc46e3..44469d8 100644 --- a/inventory-web/src/views/stock/inbound/semi.vue +++ b/inventory-web/src/views/stock/inbound/semi.vue @@ -393,10 +393,7 @@ - + @@ -664,7 +661,6 @@ import WarehouseSelector from '@/components/WarehouseSelector.vue' import SmartScannerDialog from '@/components/SmartScannerDialog.vue' import TrackScanDialog from '@/components/TrackScanDialog.vue' import {getLabelPreview, executePrint} from '@/api/common/print' -import { getWarehouseTree } from '@/api/common/warehouse' import { usePasteUpload } from '@/hooks/usePasteUpload' import { useUserStore } from '@/stores/user' @@ -826,9 +822,6 @@ const scannerDialogVisible = ref(false) // 顶部扫码入库面板 (Track 身份证自动填充物料+序列号) const trackScanVisible = ref(false) -// 库位级联选择器数据 -const warehouseOptions = ref([]) - const entryMode = ref('batch') const modeLocked = ref(false) @@ -1312,17 +1305,6 @@ const buildCategoryTree = (categories: string[]) => { return root; }; -// 加载库位树数据 -const loadWarehouseTree = async () => { - try { - const res = await getWarehouseTree() - if (res.code === 200) { - warehouseOptions.value = res.data || [] - } - } catch (e) { - console.error('加载库位树失败', e) - } -} const resetQuery = () => { queryParams.keyword = '' @@ -1649,7 +1631,6 @@ onMounted(() => { initColumnPermissions() fetchData() fetchOptions() - loadWarehouseTree() })