diff --git a/backend/app/api/menu.py b/backend/app/api/menu.py
index a081e5df..4ecacc14 100644
--- a/backend/app/api/menu.py
+++ b/backend/app/api/menu.py
@@ -78,6 +78,17 @@ STATIC_MENU_TREE = [
"component": "/cell-storage/sample-info/index",
"meta": {"title": "样本信息管理", "keepAlive": True, "menuCode": "cell-storage:sample-info:list"},
},
+ {
+ "id": "50000000000000000000000000000021",
+ "name": "CellStorageSampleFreight",
+ "path": "sample-freight",
+ "component": "/cell-storage/sample-freight/index",
+ "meta": {
+ "title": "运费信息",
+ "keepAlive": True,
+ "menuCode": "cell-storage:sample-freight:list",
+ },
+ },
{
"id": "50000000000000000000000000000004",
"name": "CellStoragePaymentConfirm",
diff --git a/docs/sql/migrations/2026-10-08-sample-freight-menu.sql b/docs/sql/migrations/2026-10-08-sample-freight-menu.sql
new file mode 100644
index 00000000..77506ad3
--- /dev/null
+++ b/docs/sql/migrations/2026-10-08-sample-freight-menu.sql
@@ -0,0 +1,72 @@
+-- =============================================================================
+-- 运费信息菜单:插入 sys_menu,并授权给已有「样本信息管理」的角色
+-- 菜单位置:样本信息管理 之下、缴费财务确认及开票 之上
+-- id: 50000000000000000000000000000021
+-- perms: cell-storage:sample-freight:list
+-- =============================================================================
+
+INSERT INTO sys_menu (
+ id, menu_name, parent_id, menu_type, path, component, perms,
+ is_frame, visible, status, tenant_no, order_num, remark,
+ create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+ '50000000000000000000000000000021',
+ '运费信息',
+ 'CEL00000000000000000000000000001',
+ 'C',
+ 'sample-freight',
+ '/cell-storage/sample-freight/index',
+ 'cell-storage:sample-freight:list',
+ 1,
+ '1',
+ '1',
+ 'zkxw',
+ (SELECT order_num + 1 FROM sys_menu WHERE id = '50000000000000000000000000000013' AND del_flag = 0 LIMIT 1),
+ '细胞存储:运费信息',
+ NULL,
+ '',
+ NOW(),
+ NULL,
+ NOW(),
+ 0
+FROM DUAL
+WHERE NOT EXISTS (
+ SELECT 1 FROM sys_menu m
+ WHERE m.del_flag = 0
+ AND (
+ m.id = '50000000000000000000000000000021'
+ OR m.perms = 'cell-storage:sample-freight:list'
+ )
+);
+
+-- 已有样本信息管理权限的角色补授权(幂等)
+INSERT INTO sys_role_menu (
+ id, role_id, menu_id, tenant_no, order_num, remark,
+ create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+ MD5(CONCAT(r.id, '|50000000000000000000000000000021')),
+ r.id,
+ '50000000000000000000000000000021',
+ IFNULL(NULLIF(r.tenant_no, ''), 'zkxw'),
+ 0,
+ 'grant sample-freight to sample-info roles',
+ NULL,
+ '',
+ NOW(),
+ NULL,
+ NOW(),
+ 0
+FROM sys_role r
+INNER JOIN sys_role_menu rm
+ ON rm.role_id = r.id
+ AND rm.menu_id = '50000000000000000000000000000013'
+ AND rm.del_flag = 0
+WHERE r.del_flag = 0
+ AND NOT EXISTS (
+ SELECT 1 FROM sys_role_menu x
+ WHERE x.role_id = r.id
+ AND x.menu_id = '50000000000000000000000000000021'
+ AND x.del_flag = 0
+ );
diff --git a/web-admin/src/router/modules/sqxp.ts b/web-admin/src/router/modules/sqxp.ts
index f4ff7330..d79664a7 100644
--- a/web-admin/src/router/modules/sqxp.ts
+++ b/web-admin/src/router/modules/sqxp.ts
@@ -93,6 +93,17 @@ export const sqxpAdminRoutes: AppRouteRecord[] = [
menuCode: 'cell-storage:sample-info:list'
}
},
+ {
+ id: '50000000000000000000000000000021',
+ name: 'CellStorageSampleFreight',
+ path: 'sample-freight',
+ component: '/cell-storage/sample-freight/index',
+ meta: {
+ title: '运费信息',
+ keepAlive: true,
+ menuCode: 'cell-storage:sample-freight:list'
+ }
+ },
{
id: '50000000000000000000000000000004',
name: 'CellStoragePaymentConfirm',
diff --git a/web-admin/src/views/cell-storage/sample-freight/index.vue b/web-admin/src/views/cell-storage/sample-freight/index.vue
new file mode 100644
index 00000000..471bc607
--- /dev/null
+++ b/web-admin/src/views/cell-storage/sample-freight/index.vue
@@ -0,0 +1,18 @@
+
+
+
+
+