From 68fd93b457dabd4e9946b71e06440c3e1099f5b2 Mon Sep 17 00:00:00 2001 From: yueli Date: Tue, 14 Jul 2026 17:02:07 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20BOM=E8=B7=B3=E8=BD=AC=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=20+=20=E9=87=87=E8=B4=AD=E5=8D=95=E5=AF=BC=E5=85=A5=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20+=20=E5=9F=BA=E7=A1=80=E4=BF=A1=E6=81=AF=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E7=A6=81=E7=94=A8=E5=AE=88=E5=8D=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BOM: 移除父件/子件跳转按钮的 isReadOnlyMode 限制, 任何模式均可跳转 - BOM: 补全缺失的5个权限码(sys_element + sys_role_permission) - 采购单导入: supplier_link→detail_link, 新增采购人/邮箱自动填充 - 采购单导入: 实时搜索(300ms防抖)+模糊匹配, 保留搜索按钮 - 采购单导入: 移除原始/详情链接的历史 autocomplete - 基础信息: el-form 增加 formDisabled, 无 material_list:operation 时全局禁用 - /approved-unstocked API 增加 permission_required(inbound_buy) - PurchaseRequest.to_dict() 增加 requester_email --- inventory-backend/app/api/v1/purchase.py | 1 + inventory-backend/fix_bom_perms.py | 44 +++++++++++++++++++++++ inventory-web/src/views/bom/BomManage.vue | 4 +-- inventory-web/src/views/material/list.vue | 10 ++++-- 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 inventory-backend/fix_bom_perms.py diff --git a/inventory-backend/app/api/v1/purchase.py b/inventory-backend/app/api/v1/purchase.py index 04ebffb..2c21746 100644 --- a/inventory-backend/app/api/v1/purchase.py +++ b/inventory-backend/app/api/v1/purchase.py @@ -213,6 +213,7 @@ def auto_fill_purchase(): # -------------------------------------------------------- @purchase_bp.route('/approved-unstocked', methods=['GET']) @jwt_required() +@permission_required('inbound_buy') def get_approved_unstocked_requests(): """获取已审批通过且未入库的采购申请列表""" try: diff --git a/inventory-backend/fix_bom_perms.py b/inventory-backend/fix_bom_perms.py new file mode 100644 index 0000000..cc1e594 --- /dev/null +++ b/inventory-backend/fix_bom_perms.py @@ -0,0 +1,44 @@ +"""补全 BOM 模块缺失的权限码""" +import subprocess, sys + +CODES = [ + ('bom_manage:parent_spec', '父件规格'), + ('bom_manage:child_id', '子件ID'), + ('bom_manage:dosage', '用量'), + ('bom_manage:remark', '备注'), +] + +SQLS = [] +# 1. sys_element +for code, name in CODES: + SQLS.append(f"""INSERT INTO sys_element (menu_code, name, code, element_type) +SELECT 'bom_manage', '{name}', '{code}', 'column' +WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = '{code}');""") + +# 2. sys_role_permission — 给已有 BOM 权限的角色补全 +SQLS.append(""" +INSERT INTO sys_role_permission (role_code, target_code, type, company_name) +SELECT rp.role_code, elem.code, 'element', rp.company_name +FROM (SELECT DISTINCT role_code, company_name FROM sys_role_permission WHERE target_code LIKE 'bom_manage:%') rp +CROSS JOIN (SELECT unnest(ARRAY['bom_manage:parent_spec','bom_manage:child_id','bom_manage:dosage','bom_manage:remark']) AS code) elem +WHERE NOT EXISTS ( + SELECT 1 FROM sys_role_permission rp2 + WHERE rp2.role_code = rp.role_code + AND rp2.target_code = elem.code + AND COALESCE(rp2.company_name, '') = COALESCE(rp.company_name, '') +); +""") + +# 3. 验证 +SQLS.append("""SELECT target_code, string_agg(role_code, ', ' ORDER BY role_code) AS roles +FROM sys_role_permission WHERE target_code LIKE 'bom_manage:%' +GROUP BY target_code ORDER BY target_code;""") + +full_sql = '\n'.join(SQLS) +result = subprocess.run( + ['docker', 'exec', '-i', 'inventory_db', 'psql', '-U', 'test', '-d', 'inventory_system'], + input=full_sql, capture_output=True, text=True, timeout=10 +) +print(result.stdout) +if result.stderr: + print(result.stderr, file=sys.stderr) diff --git a/inventory-web/src/views/bom/BomManage.vue b/inventory-web/src/views/bom/BomManage.vue index 39fc510..21b4435 100644 --- a/inventory-web/src/views/bom/BomManage.vue +++ b/inventory-web/src/views/bom/BomManage.vue @@ -93,7 +93,7 @@ - + - + @@ -604,7 +604,7 @@ @@ -1048,6 +1048,12 @@ const hasFieldPermission = (field: string) => { return userStore.hasPermission(code); }; +// 表单全局禁用:没有 material_list:operation 权限时,所有表单字段不可编辑 +const formDisabled = computed(() => { + if (userStore.role === 'SUPER_ADMIN' || userStore.username === 'IRIS') return false; + return !userStore.hasPermission('material_list:operation'); +}); + const companyOptions = ref([]); const categoryOptions = ref([]); const typeOptions = ref([]);