diff --git a/inventory-backend/app/api/v1/inbound/base.py b/inventory-backend/app/api/v1/inbound/base.py index 5d7ed86..0ec2cc4 100644 --- a/inventory-backend/app/api/v1/inbound/base.py +++ b/inventory-backend/app/api/v1/inbound/base.py @@ -53,13 +53,9 @@ def get_current_user_permissions(): def filter_item_by_permissions(item_dict, user_permissions): - """ - 根据用户权限过滤 item 字典,无权限的字段值置为 None - """ - # 如果用户拥有通配符权限,则不过滤 + """根据用户权限过滤字段,无权限的字段值置为 None""" if 'material_list:*' in user_permissions: return item_dict - # 字段名到权限码的映射(与前端 permissionMap 保持一致) field_to_perm = { 'id': 'material_list:id', 'companyName': 'material_list:companyName', @@ -74,7 +70,11 @@ def filter_item_by_permissions(item_dict, user_permissions): 'generalManual': 'material_list:files', 'generalImage': 'material_list:files', 'referencePrice': 'material_list:referencePrice', - 'isEnabled': 'material_list:isEnabled' + 'isEnabled': 'material_list:isEnabled', + 'isInspectionRequired': 'material_list:isInspectionRequired', + 'visibilityLevel': 'material_list:visibilityLevel', + 'manualLinkRemark': 'material_list:manualLinkRemark', + 'productImageRemark': 'material_list:productImageRemark', } for field, perm_code in field_to_perm.items(): if field in item_dict and perm_code not in user_permissions: diff --git a/inventory-backend/app/api/v1/inbound/buy.py b/inventory-backend/app/api/v1/inbound/buy.py index 9ac5458..4f171f3 100644 --- a/inventory-backend/app/api/v1/inbound/buy.py +++ b/inventory-backend/app/api/v1/inbound/buy.py @@ -34,44 +34,69 @@ def get_current_user_permissions(): def filter_item_by_permissions(item_dict, user_permissions): """ - 根据用户权限过滤 item 字典,无权限的字段值置为 None + 根据用户权限过滤字段,无权限的字段值置为 None。 + 所有字段均可通过权限码独立控制。 """ - # 字段名到权限码的映射(与前端 permissionMap 保持一致) + # 字段名 → 权限码(与前端 permissionMap、数据库 sys_element/sys_menu 保持一致) field_to_perm = { + # 基础身份 'id': 'inbound_buy:id', 'base_id': 'inbound_buy:base_id', 'global_print_id': 'inbound_buy:global_print_id', - 'sku': 'inbound_buy:sku', - 'barcode': 'inbound_buy:barcode', - 'in_date': 'inbound_buy:in_date', - 'serial_number': 'inbound_buy:sn_bn', - 'batch_number': 'inbound_buy:sn_bn', - 'status': 'inbound_buy:status', - 'in_quantity': 'inbound_buy:in_quantity', - 'stock_quantity': 'inbound_buy:stock_quantity', - 'available_quantity': 'inbound_buy:available_quantity', - 'inspection_status': 'inbound_buy:inspection_status', - 'warehouse_location': 'inbound_buy:warehouse_location', - 'unit_price': 'inbound_buy:unit_price', - 'post_tax_unit_price': 'inbound_buy:post_tax_unit_price', - 'tax_rate': 'inbound_buy:tax_rate', - 'total_price': 'inbound_buy:total_price', - 'currency': 'inbound_buy:currency', - 'exchange_rate': 'inbound_buy:exchange_rate', - 'supplier_name': 'inbound_buy:supplier_name', - 'buyer_name': 'inbound_buy:buyer_name', - 'buyer_email': 'inbound_buy:buyer_email', - 'original_link': 'inbound_buy:original_link', - 'detail_link': 'inbound_buy:detail_link', - 'arrival_photo': 'inbound_buy:arrival_photo', - 'inspection_report': 'inbound_buy:inspection_report', + 'global_print_id_str': 'inbound_buy:global_print_id', + 'company_name': 'inbound_buy:company_name', 'material_name': 'inbound_buy:material_name', 'spec_model': 'inbound_buy:spec_model', 'category': 'inbound_buy:category', 'unit': 'inbound_buy:unit', 'material_type': 'inbound_buy:material_type', - 'company_name': 'inbound_buy:company_name', + 'isInspectionRequired': 'inbound_buy:isInspectionRequired', + # 入库身份 + 'sku': 'inbound_buy:sku', + 'inbound_date': 'inbound_buy:inbound_date', + 'barcode': 'inbound_buy:barcode', + 'serial_number': 'inbound_buy:sn_bn', + 'batch_number': 'inbound_buy:sn_bn', + 'warehouse_loc': 'inbound_buy:warehouse_loc', + 'warehouse_location': 'inbound_buy:warehouse_loc', + # 状态 + 'status': 'inbound_buy:status', + 'inspection_status': 'inbound_buy:inspection_status', + # 数量(三组命名,覆盖 model to_dict 中所有 key) + 'in_quantity': 'inbound_buy:in_quantity', + 'qty_inbound': 'inbound_buy:in_quantity', + 'stock_quantity': 'inbound_buy:stock_quantity', + 'qty_stock': 'inbound_buy:stock_quantity', + 'available_quantity': 'inbound_buy:available_quantity', + 'qty_available': 'inbound_buy:available_quantity', + # 价格 + 'unit_price': 'inbound_buy:unit_price', + 'post_tax_unit_price': 'inbound_buy:post_tax_unit_price', + 'total_price': 'inbound_buy:total_price', + 'tax_rate': 'inbound_buy:tax_rate', + 'currency': 'inbound_buy:currency', + 'exchange_rate': 'inbound_buy:exchange_rate', + # 商务 + 'supplier_name': 'inbound_buy:supplier_name', + 'purchaser': 'inbound_buy:purchaser', + 'purchaser_email': 'inbound_buy:purchaser_email', + 'source_link': 'inbound_buy:source_link', + 'detail_link': 'inbound_buy:detail_link', + # 图片/附件 + 'arrival_photo': 'inbound_buy:arrival_photo', + 'inspection_report': 'inbound_buy:inspection_report', + # 采购单关联 + 'request_id': 'inbound_buy:request_id', + 'request_no': 'inbound_buy:request_no', } + # 通配符(SUPER_ADMIN)不过滤 + if 'inbound_buy:*' in user_permissions: + return item_dict + for field, perm_code in field_to_perm.items(): + base_perm_code = perm_code.split(':')[-1] if ':' in perm_code else perm_code + if field in item_dict and perm_code not in user_permissions and base_perm_code not in user_permissions: + item_dict[field] = None + return item_dict # 如果用户是超级管理员且有 'inbound_buy:*',则不过滤 if 'inbound_buy:*' in user_permissions: return item_dict diff --git a/inventory-backend/app/api/v1/inbound/product.py b/inventory-backend/app/api/v1/inbound/product.py index c50b489..aa416da 100644 --- a/inventory-backend/app/api/v1/inbound/product.py +++ b/inventory-backend/app/api/v1/inbound/product.py @@ -19,6 +19,7 @@ def get_current_user_permissions(): return perm_dict.get('menus', []) + perm_dict.get('elements', []) def filter_item_by_permissions(item_dict, user_permissions): + """根据用户权限过滤字段,无权限的字段值置为 None""" field_to_perm = { 'id': 'inbound_product:id', 'base_id': 'inbound_product:base_id', 'company_name': 'inbound_product:company_name', 'material_name': 'inbound_product:material_name', 'category': 'inbound_product:category', diff --git a/inventory-backend/app/api/v1/inbound/semi.py b/inventory-backend/app/api/v1/inbound/semi.py index 94c2eda..74bc424 100644 --- a/inventory-backend/app/api/v1/inbound/semi.py +++ b/inventory-backend/app/api/v1/inbound/semi.py @@ -19,6 +19,7 @@ def get_current_user_permissions(): return perm_dict.get('menus', []) + perm_dict.get('elements', []) def filter_item_by_permissions(item_dict, user_permissions): + """根据用户权限过滤字段,无权限的字段值置为 None""" field_to_perm = { 'id': 'inbound_semi:id', 'base_id': 'inbound_semi:base_id', 'company_name': 'inbound_semi:company_name', 'material_name': 'inbound_semi:material_name', 'category': 'inbound_semi:category', diff --git a/inventory-backend/app/api/v1/inbound/service.py b/inventory-backend/app/api/v1/inbound/service.py index 8248f10..33f52b0 100644 --- a/inventory-backend/app/api/v1/inbound/service.py +++ b/inventory-backend/app/api/v1/inbound/service.py @@ -32,10 +32,7 @@ def get_current_user_permissions(): def filter_item_by_permissions(item_dict, user_permissions): - """ - 根据用户权限过滤 item 字典,无权限的字段值置为 None - """ - # 字段名到权限码的映射(与前端 permissionMap 保持一致) + """根据用户权限过滤字段,无权限的字段值置为 None""" field_to_perm = { 'id': 'inbound_service:id', 'base_id': 'inbound_service:base_id', diff --git a/inventory-backend/app/api/v1/permission.py b/inventory-backend/app/api/v1/permission.py index 9d3dfd9..a06e1c8 100644 --- a/inventory-backend/app/api/v1/permission.py +++ b/inventory-backend/app/api/v1/permission.py @@ -13,6 +13,17 @@ def _get_operator_company(): role = claims.get('role', '') if role and role.upper() == 'SUPER_ADMIN': return None # 超管不限制公司 + + +def _has_system_permission(role_code): + """检查角色是否有 system_permission""" + try: + from app.services.auth_service import AuthService + perm_dict = AuthService.get_user_permissions(role_code) + all_perms = perm_dict.get('menus', []) + perm_dict.get('elements', []) + return 'system_permission' in all_perms + except Exception: + return False return claims.get('company_name', '') @@ -31,10 +42,20 @@ def get_tree(): @permission_bp.route('/role/', methods=['GET']) @jwt_required() -@permission_required('system_permission') def get_role_perms(role_code): - """获取某个角色的权限列表(已选中的)""" + """获取某个角色的权限列表。 + - 查自己角色:不需要额外权限 + - 查其他角色:需要 system_permission + """ try: + claims = get_jwt() + current_role = (claims.get('role') or '').upper() + + # 非管理员查其他角色 → 拒绝 + if current_role != role_code.upper() and current_role != 'SUPER_ADMIN': + if not _has_system_permission(current_role): + return jsonify({'code': 403, 'msg': '无权查看其他角色的权限'}), 403 + company_name = _get_operator_company() data = PermissionService.get_role_permissions(role_code, company_name=company_name) return jsonify({'code': 200, 'msg': '获取成功', 'data': data}), 200 diff --git a/inventory-web/src/views/stock/inbound/buy.vue b/inventory-web/src/views/stock/inbound/buy.vue index 2221118..0481f4e 100644 --- a/inventory-web/src/views/stock/inbound/buy.vue +++ b/inventory-web/src/views/stock/inbound/buy.vue @@ -513,19 +513,19 @@ - + - + - + @@ -537,7 +537,7 @@ - + - + - + - + @@ -1861,27 +1861,26 @@ const confirmPurchaseImport = () => { return } - // 2. 填充采购商务信息(单价/总价互相推算,缺哪个补哪个) + // 2. 填充采购商务信息 const qty = Number(po.quantity) || 1 form.in_quantity = qty - const hasUnitPrice = po.unit_price !== null && po.unit_price !== undefined && Number(po.unit_price) > 0 - const hasTotalPrice = po.total_price !== null && po.total_price !== undefined && Number(po.total_price) > 0 + // 价格字段:仅具有单价查看权限的用户才自动填充 + if (hasFormFieldPermission('unit_price')) { + const hasUnitPrice = po.unit_price !== null && po.unit_price !== undefined && Number(po.unit_price) > 0 + const hasTotalPrice = po.total_price !== null && po.total_price !== undefined && Number(po.total_price) > 0 - if (hasUnitPrice && hasTotalPrice) { - // 两者都有,直接使用 - form.unit_price = Number(po.unit_price) - form.total_price = Number(po.total_price) - } else if (hasUnitPrice) { - // 只有单价 → 用数量推算总价 - form.unit_price = Number(po.unit_price) - form.total_price = Number((qty * form.unit_price).toFixed(2)) - } else if (hasTotalPrice) { - // 只有总价 → 用数量反推单价 - form.total_price = Number(po.total_price) - form.unit_price = Number((form.total_price / qty).toFixed(4)) + if (hasUnitPrice && hasTotalPrice) { + form.unit_price = Number(po.unit_price) + form.total_price = Number(po.total_price) + } else if (hasUnitPrice) { + form.unit_price = Number(po.unit_price) + form.total_price = Number((qty * form.unit_price).toFixed(2)) + } else if (hasTotalPrice) { + form.total_price = Number(po.total_price) + form.unit_price = Number((form.total_price / qty).toFixed(4)) + } } - // 都没有就都不填 if (po.supplier_link) { form.source_link = po.supplier_link }