fix: BOM跳转恢复 + 采购单导入优化 + 基础信息表单禁用守卫
- 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
This commit is contained in:
@ -213,6 +213,7 @@ def auto_fill_purchase():
|
|||||||
# --------------------------------------------------------
|
# --------------------------------------------------------
|
||||||
@purchase_bp.route('/approved-unstocked', methods=['GET'])
|
@purchase_bp.route('/approved-unstocked', methods=['GET'])
|
||||||
@jwt_required()
|
@jwt_required()
|
||||||
|
@permission_required('inbound_buy')
|
||||||
def get_approved_unstocked_requests():
|
def get_approved_unstocked_requests():
|
||||||
"""获取已审批通过且未入库的采购申请列表"""
|
"""获取已审批通过且未入库的采购申请列表"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
44
inventory-backend/fix_bom_perms.py
Normal file
44
inventory-backend/fix_bom_perms.py
Normal file
@ -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)
|
||||||
@ -93,7 +93,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-autocomplete>
|
</el-autocomplete>
|
||||||
<el-link
|
<el-link
|
||||||
v-if="form.parent_id && !isReadOnlyMode"
|
v-if="form.parent_id"
|
||||||
type="primary"
|
type="primary"
|
||||||
:underline="false"
|
:underline="false"
|
||||||
style="margin-left: 12px; font-size: 13px;"
|
style="margin-left: 12px; font-size: 13px;"
|
||||||
@ -183,7 +183,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-autocomplete>
|
</el-autocomplete>
|
||||||
<el-tooltip content="前往修改基础信息" placement="top" v-if="row.child_id && !isReadOnlyMode">
|
<el-tooltip content="前往修改基础信息" placement="top" v-if="row.child_id">
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
link
|
link
|
||||||
|
|||||||
@ -408,7 +408,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
|
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px" :disabled="formDisabled">
|
||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
@ -604,7 +604,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="cancel" :disabled="isUploading">取 消</el-button>
|
<el-button @click="cancel" :disabled="isUploading">取 消</el-button>
|
||||||
<el-button type="primary" @click="submitForm" :loading="submitLoading || isUploading">确 定</el-button>
|
<el-button type="primary" @click="submitForm" :loading="submitLoading || isUploading" :disabled="formDisabled">确 定</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
@ -1048,6 +1048,12 @@ const hasFieldPermission = (field: string) => {
|
|||||||
return userStore.hasPermission(code);
|
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<string[]>([]);
|
const companyOptions = ref<string[]>([]);
|
||||||
const categoryOptions = ref<string[]>([]);
|
const categoryOptions = ref<string[]>([]);
|
||||||
const typeOptions = ref<string[]>([]);
|
const typeOptions = ref<string[]>([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user