diff --git a/inventory-backend/app/api/v1/transactions.py b/inventory-backend/app/api/v1/transactions.py
index 1228c93..3654d3e 100644
--- a/inventory-backend/app/api/v1/transactions.py
+++ b/inventory-backend/app/api/v1/transactions.py
@@ -1,14 +1,64 @@
from flask import Blueprint, jsonify, request # .material -> .base refactor checked
-from flask_jwt_extended import jwt_required, get_jwt_identity
+from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt
+from app.utils.decorators import permission_required
+from app.services.auth_service import AuthService
from app.services.trans_service import TransService
import traceback
trans_bp = Blueprint('transactions', __name__, url_prefix='/transactions')
+# ==============================================================================
+# 辅助函数:获取当前用户的完整权限列表(基于角色查询)
+# ==============================================================================
+def get_current_user_permissions():
+ """
+ 返回当前用户拥有的所有权限码列表(包括菜单和元素)
+ 此函数根据角色查询数据库得到权限。
+ """
+ claims = get_jwt()
+ user_role = claims.get('role')
+ if not user_role:
+ return []
+ # 超级管理员返回所有字段权限
+ if user_role == 'super_admin':
+ return ['*']
+ perm_dict = AuthService.get_user_permissions(user_role)
+ # 合并菜单和元素权限
+ perms = perm_dict.get('menus', []) + perm_dict.get('elements', [])
+ return perms
+
+
+def filter_item_by_permissions(item_dict, user_permissions, prefix='op_records'):
+ """
+ 根据用户权限过滤 item 字典,无权限的字段值置为 None
+ """
+ # 字段名到权限码的映射(与前端 permissionMap 保持一致)
+ field_to_perm = {
+ 'borrow_no': f'{prefix}:borrow_no',
+ 'borrower_name': f'{prefix}:borrower_name',
+ 'sku': f'{prefix}:sku',
+ 'borrow_time': f'{prefix}:borrow_time',
+ 'return_time': f'{prefix}:return_time',
+ 'status': f'{prefix}:status',
+ 'expected_return_time': f'{prefix}:expected_return_time',
+ 'return_location': f'{prefix}:return_location',
+ 'borrow_signature': f'{prefix}:borrow_signature',
+ 'return_signature': f'{prefix}:return_signature',
+ }
+ # 如果用户是超级管理员且有 '*',则不过滤
+ if '*' in user_permissions:
+ return item_dict
+ for field, perm_code in field_to_perm.items():
+ if field in item_dict and perm_code not in user_permissions:
+ item_dict[field] = None
+ return item_dict
+
+
# --- 借库接口 ---
@trans_bp.route('/borrow', methods=['POST'])
@jwt_required()
+@permission_required('op_borrow:operation')
def create_borrow():
data = request.get_json()
try:
@@ -21,6 +71,7 @@ def create_borrow():
# --- 还库辅助:扫码查找借出记录 ---
@trans_bp.route('/return/scan', methods=['GET'])
@jwt_required()
+@permission_required('op_return')
def scan_borrowed_item():
barcode = request.args.get('barcode')
if not barcode:
@@ -36,6 +87,7 @@ def scan_borrowed_item():
# --- 还库提交 ---
@trans_bp.route('/return', methods=['POST'])
@jwt_required()
+@permission_required('op_return:operation')
def submit_return():
data = request.get_json()
user = get_jwt_identity() # 库管
@@ -49,10 +101,15 @@ def submit_return():
# --- 记录列表 ---
@trans_bp.route('/records', methods=['GET'])
@jwt_required()
+@permission_required('op_records')
def get_records():
status = request.args.get('status', 'all')
page = int(request.args.get('page', 1))
keyword = request.args.get('keyword', '')
res = TransService.get_records(page=page, limit=10, status=status, keyword=keyword)
+ # 字段级脱敏
+ user_permissions = get_current_user_permissions()
+ if res.get('items'):
+ res['items'] = [filter_item_by_permissions(item, user_permissions, 'op_records') for item in res['items']]
return jsonify({'code': 200, 'data': res})
diff --git a/inventory-web/src/views/transaction/borrow.vue b/inventory-web/src/views/transaction/borrow.vue
index 0c9e5b8..f8b5606 100644
--- a/inventory-web/src/views/transaction/borrow.vue
+++ b/inventory-web/src/views/transaction/borrow.vue
@@ -13,10 +13,14 @@
-
+
点击开启全屏扫码
+
+
+ 无扫码权限
+
- 添加
+ 添加
@@ -40,16 +45,16 @@
-
-
+
+
-
+
{{ parseFloat(row.available_quantity) }}
-
+
-
+
@@ -102,7 +108,7 @@
-
+
点击重签
@@ -112,11 +118,17 @@
点击此处进行全屏签名
+
- 清空
-
+ 清空
+
确认借出
@@ -187,6 +199,27 @@ import QrScanner from '@/components/QrScanner/index.vue'
import { getStockByBarcode } from '@/api/outbound'
import request from '@/utils/request'
import { uploadFile } from '@/api/common/upload'
+import { useUserStore } from '@/stores/user'
+
+const userStore = useUserStore()
+
+// 列与权限Code的映射关系(数据库中的code)
+const permissionMap: Record
= {
+ borrower_name: 'op_borrow:borrower_name',
+ sku: 'op_borrow:sku',
+ available_quantity: 'op_borrow:available_quantity',
+ out_quantity: 'op_borrow:out_quantity',
+ // 其他字段可根据需要添加
+}
+
+// 检查列权限
+const hasColumnPermission = (prop: string) => {
+ if (userStore.role === 'SUPER_ADMIN' || userStore.username === 'IRIS') {
+ return true
+ }
+ const code = permissionMap[prop]
+ return code ? userStore.hasPermission(code) : false
+}
// --- 状态定义 ---
const barcodeInput = ref('')
@@ -564,4 +597,4 @@ onUnmounted(() => {
.sidebar-actions { flex-direction: row; width: 100%; gap: 10px; }
.sidebar-actions .el-button { flex: 1; height: 40px; }
}
-
\ No newline at end of file
+
diff --git a/inventory-web/src/views/transaction/records.vue b/inventory-web/src/views/transaction/records.vue
index fd622a6..28f73fe 100644
--- a/inventory-web/src/views/transaction/records.vue
+++ b/inventory-web/src/views/transaction/records.vue
@@ -88,6 +88,32 @@ import request from '@/utils/request'
import dayjs from 'dayjs' // 建议使用 dayjs 处理日期,如果没有安装,可以用原生 Date
import 'dayjs/locale/zh-cn' // 导入中文包
dayjs.locale('zh-cn')
+import { useUserStore } from '@/stores/user'
+
+const userStore = useUserStore()
+
+// 列与权限Code的映射关系(数据库中的code)
+const permissionMap: Record = {
+ borrow_no: 'op_records:borrow_no',
+ borrower_name: 'op_records:borrower_name',
+ sku: 'op_records:sku',
+ borrow_time: 'op_records:borrow_time',
+ return_time: 'op_records:return_time',
+ status: 'op_records:status',
+ expected_return_time: 'op_records:expected_return_time',
+ return_location: 'op_records:return_location',
+ borrow_signature: 'op_records:borrow_signature',
+ return_signature: 'op_records:return_signature',
+}
+
+// 检查列权限
+const hasColumnPermission = (prop: string) => {
+ if (userStore.role === 'SUPER_ADMIN' || userStore.username === 'IRIS') {
+ return true
+ }
+ const code = permissionMap[prop]
+ return code ? userStore.hasPermission(code) : false
+}
const list = ref([])
const total = ref(0)
@@ -195,4 +221,4 @@ onMounted(fetchData)
.text-normal {
color: #909399;
}
-
\ No newline at end of file
+
diff --git a/inventory-web/src/views/transaction/return.vue b/inventory-web/src/views/transaction/return.vue
index a175be1..c98519d 100644
--- a/inventory-web/src/views/transaction/return.vue
+++ b/inventory-web/src/views/transaction/return.vue
@@ -13,10 +13,14 @@
-
+
点击开启全屏扫码
+
+
+ 无扫码权限
+
- 识别
+ 识别
@@ -40,16 +45,17 @@
-
-
+
+
-
+
变更
@@ -58,7 +64,7 @@
-
+
@@ -77,7 +83,7 @@
-
+
点击重签
@@ -87,12 +93,18 @@
点击此处进行库管签名
+
- 清空
-
+ 清空
+
确认归还
@@ -161,6 +173,25 @@ import { uploadFile } from '@/api/common/upload'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Scissor, EditPen, Delete, CameraFilled, Close, Refresh, Select } from '@element-plus/icons-vue'
import QrScanner from '@/components/QrScanner/index.vue'
+import { useUserStore } from '@/stores/user'
+
+const userStore = useUserStore()
+
+// 列与权限Code的映射关系(数据库中的code)
+const permissionMap: Record
= {
+ borrower_name: 'op_return:borrower_name',
+ sku: 'op_return:sku',
+ return_location: 'op_return:return_location',
+}
+
+// 检查列权限
+const hasColumnPermission = (prop: string) => {
+ if (userStore.role === 'SUPER_ADMIN' || userStore.username === 'IRIS') {
+ return true
+ }
+ const code = permissionMap[prop]
+ return code ? userStore.hasPermission(code) : false
+}
// --- 状态 ---
const barcode = ref('')
@@ -507,4 +538,4 @@ onUnmounted(() => {
.sidebar-actions { flex-direction: row; width: 100%; gap: 10px; }
.sidebar-actions .el-button { flex: 1; height: 40px; }
}
-
\ No newline at end of file
+