feat: implement dynamic inspection requirement logic based on material master data

This commit is contained in:
DXC
2026-03-17 11:56:04 +08:00
parent 368298a29d
commit c1c494893f
8 changed files with 273 additions and 14 deletions

View File

@ -146,6 +146,17 @@
</template>
</template>
<!-- 批量质检设置按钮 (需要 operation 权限) -->
<el-button
v-if="userStore.hasPermission('material_list:operation')"
type="danger"
plain
@click="openBatchInspectionDialog"
style="margin-right: 10px"
>
<el-icon style="margin-right: 5px"><CircleCheck /></el-icon>批量质检设置
</el-button>
<el-button v-if="userStore.hasPermission('material_list:operation')" type="primary" @click="handleAdd" style="margin-right: 10px">
<el-icon style="margin-right: 5px"><Plus /></el-icon>新增
</el-button>
@ -295,6 +306,13 @@
/>
</template>
</el-table-column>
<el-table-column v-if="columns.isInspectionRequired.visible" prop="isInspectionRequired" label="强制质检" min-width="100" align="center">
<template #default="scope">
<el-tag :type="scope.row.isInspectionRequired ? 'danger' : 'info'" size="small">
{{ scope.row.isInspectionRequired ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column v-if="userStore.hasPermission('material_list:operation')" label="操作" min-width="200" fixed="right" align="center">
<template #default="scope">
<el-button v-if="userStore.hasPermission('material_list:operation')" link type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
@ -526,13 +544,41 @@
</div>
</template>
</el-dialog>
<!-- 批量质检设置弹窗 -->
<el-dialog v-model="inspectionDialog.visible" title="批量质检设置" width="500px" append-to-body destroy-on-close>
<el-alert
:title="`已选择 ${inspectionDialog.selectedCount} 条物料进行批量质检设置`"
type="info"
:closable="false"
style="margin-bottom: 20px"
/>
<el-form label-width="180px">
<el-form-item label="是否强制要求入库上传检测报告">
<el-switch
v-model="inspectionForm.isInspectionRequired"
active-text=""
inactive-text=""
/>
</el-form-item>
<div style="color: #909399; font-size: 12px; margin-top: -10px;">
开启后,这些物料在采购入库时必须上传检测报告或外部链接
</div>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="inspectionDialog.visible = false">取 消</el-button>
<el-button type="primary" @click="submitBatchInspection" :loading="inspectionLoading">确 定</el-button>
</div>
</template>
</el-dialog>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, nextTick, computed } from 'vue';
import { Plus, Document, Refresh, Setting, Rank, Camera, Link, Download, Bell } from '@element-plus/icons-vue';
import { Plus, Document, Refresh, Setting, Rank, Camera, Link, Download, Bell, CircleCheck } from '@element-plus/icons-vue';
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import type { FormInstance, FormRules } from 'element-plus';
import { useUserStore } from '@/stores/user';
@ -544,7 +590,8 @@ import {
delMaterialBase,
getMaterialBaseOptions,
exportAssetStatistics,
batchSetWarning
batchSetWarning,
batchSetInspection
} from '@/api/material_base';
import { uploadFile, deleteFile } from '@/api/common/upload';
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue';
@ -691,6 +738,17 @@ const warningRules = {
]
};
// 批量质检设置相关
const inspectionDialog = reactive({
visible: false,
selectedCount: 0,
selectedIds: [] as number[]
});
const inspectionLoading = ref(false);
const inspectionForm = reactive({
isInspectionRequired: false
});
const columns = reactive({
id: { visible: false },
companyName: { visible: true },
@ -703,7 +761,8 @@ const columns = reactive({
inventory: { visible: true },
available: { visible: true },
files: { visible: true },
isEnabled: { visible: true }
isEnabled: { visible: true },
isInspectionRequired: { visible: true }
});
// 列与权限Code的映射关系数据库中的code
@ -719,7 +778,8 @@ const permissionMap: Record<string, string> = {
inventory: 'material_list:inventoryCount',
available: 'material_list:availableCount',
files: 'material_list:files',
isEnabled: 'material_list:isEnabled'
isEnabled: 'material_list:isEnabled',
isInspectionRequired: 'material_list:operation'
};
// 根据用户权限初始化列显示状态
@ -1247,6 +1307,47 @@ const submitWarning = async () => {
}
};
// 打开批量质检设置对话框
const openBatchInspectionDialog = () => {
// 获取当前勾选的物料
const selected = tableRef.value?.getSelectionRows() || [];
if (selected.length === 0) {
ElMessage.warning('请先勾选需要设置质检的物料');
return;
}
inspectionDialog.selectedIds = selected.map((row: MaterialBaseVO) => row.id);
inspectionDialog.selectedCount = selected.length;
inspectionForm.isInspectionRequired = false; // 默认重置为否
inspectionDialog.visible = true;
};
// 提交批量质检设置
const submitBatchInspection = async () => {
if (inspectionDialog.selectedIds.length === 0) {
ElMessage.warning('请先勾选物料');
return;
}
inspectionLoading.value = true;
try {
await batchSetInspection({
ids: inspectionDialog.selectedIds,
isInspectionRequired: inspectionForm.isInspectionRequired
});
ElMessage.success('批量质检设置成功');
inspectionDialog.visible = false;
selectedItems.value = [];
tableRef.value?.clearSelection();
getList();
} catch (error: any) {
ElMessage.error(error?.msg || '设置失败');
} finally {
inspectionLoading.value = false;
}
};
// 表格行样式(根据预警状态)
const tableRowClassName = ({ row }: { row: MaterialBaseVO }) => {
// 只有拥有 view_warning 权限且有预警状态时才显示特殊样式

View File

@ -821,6 +821,10 @@ const printCopies = ref(1)
const entryMode = ref('batch')
const modeLocked = ref(false)
// 强制质检标记
const isCurrentMaterialInspectionRequired = ref(false)
const dialogImageUrl = ref('')
const dialogVisibleImage = ref(false)
const arrivalFileList = ref<any[]>([])
@ -1136,10 +1140,23 @@ const onMaterialSelected = (val: number) => {
form.category = item.category
form.unit = item.unit
form.material_type = item.type
// 保存强制质检标记
isCurrentMaterialInspectionRequired.value = item.isInspectionRequired || false
// 更新表单校验规则
updateInspectionRules()
checkHistoryAndSetMode(item.id)
}
}
// 动态更新质检相关校验规则
const updateInspectionRules = () => {
if (formRef.value) {
// 清除旧的校验结果
formRef.value.clearValidate('inspection_status')
formRef.value.clearValidate('inspection_report')
}
}
// ------------------------------------
// 校验规则
// ------------------------------------
@ -1159,12 +1176,39 @@ const validateIdentity = (rule: any, value: any, callback: any) => {
else if (entryMode.value === 'batch' && !form.batch_number && rule.field === 'batch_number') callback(new Error('批号必填'))
else callback()
}
const rules = {
base_id: [{required: true, message: '请选择物料', trigger: 'change'}],
in_quantity: [{required: true, message: '请输入数量', trigger: 'blur'}],
serial_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}],
batch_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}]
}
const rules = computed(() => {
const baseRules = {
base_id: [{required: true, message: '请选择物料', trigger: 'change'}],
in_quantity: [{required: true, message: '请输入数量', trigger: 'blur'}],
serial_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}],
batch_number: [{validator: validateIdentity, trigger: 'blur'}, {validator: validateUnique, trigger: 'blur'}]
}
// 如果当前物料需要强制质检,添加质检相关校验
if (isCurrentMaterialInspectionRequired.value) {
// 到检状态必填
baseRules.inspection_status = [
{ required: true, message: '该物料为强管控物料,必须选择到检状态', trigger: 'change' }
]
// 检测报告必填(通过自定义校验规则:文件或外部链接至少有一个)
baseRules.inspection_report = [
{
validator: (rule: any, value: any, callback: any) => {
const hasFile = form.inspection_report && form.inspection_report.length > 0
const hasLink = inspection_report_url.value && inspection_report_url.value.trim() !== ''
if (!hasFile && !hasLink) {
callback(new Error('该物料为强管控物料,必须提供检测报告文件或链接'))
} else {
callback()
}
},
trigger: 'blur'
}
]
}
return baseRules
})
const checkHistoryAndSetMode = async (baseId: number) => {
try {
@ -1366,7 +1410,10 @@ const handleUpdate = (row: any) => {
inspection_report_url.value = reportLinks.length > 0 ? reportLinks[0] : ''
if (row.serial_number) { entryMode.value = 'serial'; form.serial_number = row.serial_number; form.batch_number = '' }
else { entryMode.value = 'batch'; form.batch_number = row.batch_number; form.serial_number = '' }
materialOptions.value = [{ id: row.base_id, name: row.material_name, spec: row.spec_model, category: row.category, company_name: row.company_name }]
materialOptions.value = [{ id: row.base_id, name: row.material_name, spec: row.spec_model, category: row.category, company_name: row.company_name, isInspectionRequired: row.isInspectionRequired }]
// 设置强制质检标记
isCurrentMaterialInspectionRequired.value = row.isInspectionRequired || false
updateInspectionRules()
visible.value = true
}
@ -1598,6 +1645,8 @@ const confirmPrint = async () => {
const resetForm = () => {
materialOptions.value = []; arrivalFileList.value = []; reportFileList.value = []; inspection_report_url.value = ''
searchPage.value = 1; hasNextPage.value = true; searchKeyword.value = '';
// 重置强制质检标记
isCurrentMaterialInspectionRequired.value = false
Object.assign(form, {
id: undefined, base_id: undefined,
company_name: '',