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 @@
-
+
末级
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()
})