diff --git a/inventory-backend/app/api/v1/inbound/base.py b/inventory-backend/app/api/v1/inbound/base.py
index 754e7d7..c4462bb 100644
--- a/inventory-backend/app/api/v1/inbound/base.py
+++ b/inventory-backend/app/api/v1/inbound/base.py
@@ -455,3 +455,21 @@ def batch_set_inspection():
db.session.rollback()
current_app.logger.error(f"批量设置强制质检失败: {str(e)}")
return jsonify({"code": 500, "msg": f"批量设置强制质检失败: {str(e)}"}), 500
+
+
+# ==============================================================================
+# 2.7 智能分组求最大连号 API (GET /api/v1/inbound/base/spec-latest)
+# ==============================================================================
+@inbound_base_bp.route('/spec-latest', methods=['GET'])
+@permission_required('material_list')
+def get_spec_latest():
+ """
+ 获取所有规格型号的最大连号,按智能分组返回
+ 返回格式: [{"group": "S", "latest": "S0115/S0115"}, {"group": "Opt4xxx", "latest": "Opt4018/Opt4018"}, ...]
+ """
+ try:
+ data = MaterialBaseService.get_latest_specs()
+ return jsonify({"code": 200, "msg": "success", "data": data})
+ except Exception as e:
+ traceback.print_exc()
+ return jsonify({"code": 500, "msg": str(e)}), 500
diff --git a/inventory-backend/app/services/inbound/base_service.py b/inventory-backend/app/services/inbound/base_service.py
index fa67298..0c0625d 100644
--- a/inventory-backend/app/services/inbound/base_service.py
+++ b/inventory-backend/app/services/inbound/base_service.py
@@ -954,4 +954,71 @@ class MaterialBaseService:
except Exception as e:
traceback.print_exc()
- raise e
\ No newline at end of file
+ raise e
+
+ @staticmethod
+ def get_latest_specs():
+ """
+ 获取所有规格型号的最大连号,按智能分组返回
+ 返回格式: [{"group": "S", "latest": "S0115/S0115"}, {"group": "Opt4xxx", "latest": "Opt4018/Opt4018"}, ...]
+ """
+ import re
+
+ # 1. 查询所有不为空的规格型号
+ specs = MaterialBase.query.filter(
+ MaterialBase.spec_model.isnot(None),
+ MaterialBase.spec_model != ''
+ ).all()
+
+ # 2. 数据结构:{分组名: (原始规格, 数字部分)}
+ groups = {}
+
+ def parse_spec(spec_full):
+ """
+ 解析规格型号
+ 返回: (prefix, num, group_name, original_spec)
+ """
+ # 取斜杠前的部分作为基准
+ base_spec = spec_full.split('/')[0]
+
+ # 使用正则解析:字母前缀 + 数字
+ match = re.match(r'^([A-Za-z]+)(\d+)$', base_spec)
+ if not match:
+ return (base_spec, 0, base_spec, spec_full)
+
+ prefix, num_str = match.groups()
+ num = int(num_str)
+
+ # 智能分组逻辑
+ if prefix == 'Opt':
+ # Opt 按千位段分组
+ thousand = num // 1000
+ group = f"Opt{thousand}xxx"
+ else:
+ # 常规前缀按原值分组
+ group = prefix
+
+ return (prefix, num, group, spec_full)
+
+ # 3. 遍历并分组
+ for material in specs:
+ spec = material.spec_model
+ if not spec:
+ continue
+
+ prefix, num, group, original_spec = parse_spec(spec)
+
+ if group not in groups:
+ groups[group] = (original_spec, num)
+ else:
+ _, existing_num = groups[group]
+ if num > existing_num:
+ groups[group] = (original_spec, num)
+
+ # 4. 构建返回结果,按分组名排序
+ result = [
+ {"group": group, "latest": spec}
+ for group, (spec, _) in sorted(groups.items())
+ ]
+
+ return result
\ No newline at end of file
diff --git a/inventory-web/src/api/material_base.ts b/inventory-web/src/api/material_base.ts
index c690f25..dd47e4e 100644
--- a/inventory-web/src/api/material_base.ts
+++ b/inventory-web/src/api/material_base.ts
@@ -69,4 +69,12 @@ export function batchSetInspection(data: { ids: number[], isInspectionRequired:
method: 'post',
data
})
+}
+
+// 7. 获取智能分组规格最大连号
+export function getLatestSpecs() {
+ return request({
+ url: '/inbound/base/spec-latest',
+ method: 'get'
+ })
}
\ No newline at end of file
diff --git a/inventory-web/src/components/SpecHelper/index.vue b/inventory-web/src/components/SpecHelper/index.vue
new file mode 100644
index 0000000..038c94c
--- /dev/null
+++ b/inventory-web/src/components/SpecHelper/index.vue
@@ -0,0 +1,200 @@
+
+