fmea评分标准添加

This commit is contained in:
2026-09-15 10:27:08 +08:00
parent daddb87330
commit c91851e0b0
2 changed files with 155 additions and 121 deletions
+154 -120
View File
@@ -25,17 +25,19 @@ const currentModalTitle = ref<string>('');
const currentModalType = ref<string>('');
// 当前弹框对象
const currentModalObj = ref<string>('');
// 评分标准弹框
const ruleModal = ref<boolean>(false);
// 项目总体信息与过程设置弹窗
const openSetModal = ref<boolean>(false);
// 项目总体信息
const description = ref<string>('');
const nodeList = ref<any[]>([]);
const name = ref<string>('');
// 过程流程描述
// 步骤流程描述
const node_description = ref<string>('');
// 过程操作方式
// 步骤操作方式
const node_way = ref<string>('');
// 过程保护措施
// 步骤保护措施
const node_protection = ref<string>('');
// 分析列表弹窗
const openListModal = ref<boolean>(false);
@@ -47,6 +49,8 @@ const currentNodeIndex = ref<number>(-1)
// 打开分析列表弹框
const openList = () => {
currentNodeList.value = JSON.parse(JSON.stringify(nodes.value[currentNodeIndex.value]?.deviations || []))
currentNodeListSource.value = JSON.parse(JSON.stringify(currentNodeList.value))
node_description.value = currentNode.value?.description || ''
openListModal.value = true
}
@@ -236,33 +240,29 @@ const setSave = async () => {
const handleSelect = (key: string) => {
runAi(key);
}
const selectOptions = computed(() => {
const l = props.fmeaRiskMatrixConfig?.probability?.map((item:{key:string,label:string}, index:number) => ({
label: 'L'+'_'+Number(index + 1),
value: 'L'+'_'+Number(index + 1)
}));
const s = props.fmeaRiskMatrixConfig?.consequence?.map((item:{key:string,label:string}, index:number) => ({
label: 'S'+'_'+Number(index + 1),
value: 'S'+'_'+Number(index + 1)
}));
return [...l, ...s];
});
const l_options = computed(() => {
const newArr = props.fmeaRiskMatrixConfig?.probability?.map((item:{key:string,label:string}, index:number) => ({
...item,
key: Number(index + 1)
}));
return newArr;
});
console.log(props.fmeaRiskMatrixConfig)
const s_options = computed(() => {
const newArr = props.fmeaRiskMatrixConfig?.consequence?.map((item:{key:string,label:string}, index:number) => ({
...item,
key: Number(index + 1)
const newArr = props.fmeaRiskMatrixConfig?.s_grades?.map((item:any) => ({
label: item.label+'('+item.score+')',
value: item.score
}));
return newArr;
});
const p_options = computed(() => {
const newArr = props.fmeaRiskMatrixConfig?.p_grades?.map((item:any) => ({
label: item.label+'('+item.score+')',
value: item.score
}));
return newArr;
});
const d_options = computed(() => {
const newArr = props.fmeaRiskMatrixConfig?.d_grades?.map((item:any) => ({
label: item.label+'('+item.score+')',
value: item.score
}));
return newArr;
});
// 右键菜单状态
const contextMenuShow = ref<boolean>(false)
const contextMenuX = ref<number>(0)
@@ -450,85 +450,22 @@ const runInference = async () => {
}, 1000)
}
// 根据l和s计算RR风险等级
const riskLevel = (l: number, s: number) => {
const num = l * s;
for (let i = 0; i < props.fmeaRiskMatrixConfig?.risk_grades.length; i++) {
const item = props.fmeaRiskMatrixConfig?.risk_grades[i];
if (num >= item.min && num <= item.max) {
return item; // 返回整条数据
}
}
return null;
}
// 计算RR1风险等级
const countRiskLevel = (row: any) => {
const l = row.l_value;
const s = row.s_value;
const newArr = [...row?.safeguards || [],...row?.suggestions || []];
const result:any = { L: 0, S: 0 };
newArr.forEach(item => {
if (!item.value) return; // 跳过空值
const parts = item.value.split('_');
if (parts.length === 2) {
const type = parts[0].toUpperCase(); // 统一大写
const num = parseInt(parts[1], 10);
if (!isNaN(num) && (type === 'L' || type === 'S')) {
result[type] += num;
}
}
});
const newL = (l - result.L) > 0 ? l - result.L : 1;
const newS = (s - result.S) > 0 ? s - result.S : 1;
return riskLevel(newL, newS);
}
// 新增安全措施
const addSafeguard = (index: number) => {
if(currentDev.value.rows[index].safeguards){
currentDev.value.rows[index].safeguards.push({name:'',value:''});
}else{
currentDev.value.rows[index].safeguards = [{name:'',value:''}];
}
save()
}
// 删除安全措施
const delSafeguard = (index: number, safeguardIndex: number) => {
currentDev.value.rows[index].safeguards.splice(safeguardIndex, 1);
save()
}
// 新增建议措施
const addSuggestion = (index: number) => {
if(currentDev.value.rows[index].suggestions){
currentDev.value.rows[index].suggestions.push({name:'',value:''});
}else{
currentDev.value.rows[index].suggestions = [{name:'',value:''}];
}
save()
}
// 删除建议措施
const delSuggestion = (index: number, suggestionIndex: number) => {
currentDev.value.rows[index].suggestions.splice(suggestionIndex, 1);
save()
}
// 新增行
const addRow = async () => {
console.log(props.fmeaRiskMatrixConfig)
if(!currentDev.value){
return window.$message?.warning("请先在左侧选择失效模式")
}
currentDev.value.rows.push({
id: 0,
cause: "",
effect: "",
s_value: 0,
p_value: 0,
d_value: 0,
residual_level: '',
safeguards: '',
suggestions: '',
});
if(!currentDev.value){
return window.$message?.warning("请先在左侧选择失效模式")
}
currentDev.value.rows.push({
id: 0,
cause: "",
effect: "",
s_value: null,
p_value: null,
d_value: null,
residual_level: '',
safeguards: '',
suggestions: '',
});
};
// 删除行
const delRow = async (index: number) => {
@@ -859,8 +796,6 @@ onBeforeUnmount(() => {
<div class="flex items-center gap-2 text-xs text-slate-500">
<span>当前位置</span>
<!-- <NTag size="small">工作步骤{{ currentNode?.node_name ?? '未选择' }}</NTag>
<span></span> -->
<NTag size="small" type="success">工作步骤{{ currentNode?.node_name ?? '未选择' }}</NTag>
<span></span>
<NTag size="small" type="warning">失效模式{{ currentDev?.deviation ?? '未选择' }}</NTag>
@@ -894,7 +829,7 @@ onBeforeUnmount(() => {
<div v-for="(dev, devIndex) in node.deviations" :key="devIndex"
@click="clickDev(dev,Number(nodeIndex))"
@contextmenu.prevent="handleContextMenu($event, 'dev', node, dev, Number(nodeIndex), Number(devIndex))"
class="cursor-pointer flex w-full items-center ml-2 gap-1.5 rounded-md px-3 py-1.5 text-left text-xs transition"
class="cursor-pointer flex items-center ml-4 gap-1.5 rounded-md px-2 py-1.5 text-left text-xs transition"
:class="currentDev?.id === dev.id ? 'bg-[var(--theme-color)] text-white' : 'text-slate-600 hover:bg-slate-100'"
>
<span class="flex-1">{{ dev.deviation }}</span>
@@ -907,6 +842,9 @@ onBeforeUnmount(() => {
</template>
</div>
</div>
<NButton size="small" type="primary" ghost block class="mt-2 text-xs" @click="ruleModal = true">
评分标准
</NButton>
</div>
<!-- HAZOP 记录表 -->
<div class="min-w-0 flex-1 bg-white">
@@ -924,20 +862,20 @@ onBeforeUnmount(() => {
</NButton>
<span class="text-[11px] text-slate-400">风险等级按风险矩阵RPN = S × P × D 取值实时重算入表将本条选入风险分析记录表</span>
</div>
<div class="scroll">
<div class="scroll2">
<table class="w-full border-collapse text-xs">
<thead>
<tr>
<th :class="TH" class="w-16">操作</th>
<th :class="TH" class="w-60">失效后果</th>
<th :class="TH" class="w-6">S</th>
<th :class="TH" class="w-60">失效原因</th>
<th :class="TH" class="w-6">P</th>
<th :class="TH" class="w-60">现有措施</th>
<th :class="TH" class="w-6">D</th>
<th :class="TH">失效后果</th>
<th :class="TH" class="w-22">S</th>
<th :class="TH">失效原因</th>
<th :class="TH" class="w-22">P</th>
<th :class="TH">现有措施</th>
<th :class="TH" class="w-22">D</th>
<th :class="TH" class="w-20">RPN</th>
<th :class="TH" class="w-20">风险等级</th>
<th :class="TH" class="w-60">建议措施</th>
<th :class="TH">建议措施</th>
</tr>
</thead>
<tbody>
@@ -968,22 +906,22 @@ onBeforeUnmount(() => {
</div>
</td>
<td :class="TD">
<NInput type="textarea" size="small" v-model:value="row.effect" :autosize="{minRows:1}" class="text-xs" @blur="save()" />
<NInput type="textarea" size="small" v-model:value="row.effect" :autosize="{minRows:1}" class="text-xs" />
</td>
<td :class="TD" class="text-center">
<!-- <NInputNumber v-model:value="row.s_value" size="tiny" :min="s_options[0].key" :max="s_options[s_options.length-1].key" :show-button="false" placeholder="" class="text-center w-10" @blur="save()" /> -->
<NSelect v-model:value="row.s_value" size="tiny" :options="s_options" placeholder="请选择" class="text-center w-full mt-2px" />
</td>
<td :class="TD">
<NInput type="textarea" size="small" v-model:value="row.cause" :autosize="{minRows:1}" class="text-xs" @blur="save()" />
<NInput type="textarea" size="small" v-model:value="row.cause" :autosize="{minRows:1}" class="text-xs" />
</td>
<td :class="TD" class="text-center">
<!-- <NInputNumber v-model:value="row.l_value" size="tiny" :min="l_options[0].key" :max="l_options[l_options.length-1].key" :show-button="false" placeholder="" class="text-center w-10" @blur="save()" /> -->
<NSelect v-model:value="row.p_value" size="tiny" :options="p_options" placeholder="请选择" class="text-center w-full mt-2px" />
</td>
<td :class="TD">
<NInput type="textarea" size="small" v-model:value="row.safeguards" :autosize="{minRows:1}" class="text-xs" @blur="save()" />
<NInput type="textarea" size="small" v-model:value="row.safeguards" :autosize="{minRows:1}" class="text-xs" />
</td>
<td :class="TD" class="text-center">
<!-- <NInputNumber v-model:value="row.l_value" size="tiny" :min="l_options[0].key" :max="l_options[l_options.length-1].key" :show-button="false" placeholder="" class="text-center w-10" @blur="save()" /> -->
<NSelect v-model:value="row.d_value" size="tiny" :options="d_options" placeholder="请选择" class="text-center w-full mt-2px" />
</td>
<td :class="TD" class="text-center">
<!-- <NTag
@@ -1233,11 +1171,107 @@ onBeforeUnmount(() => {
</div>
</template>
</NModal>
<!-- 评分标准弹窗 -->
<NModal
v-model:show="ruleModal"
preset="card"
title="FMEA 评分标准"
:auto-focus="false"
:style="{ width: '700px', height: 'auto' }"
:segmented="{ content: true, footer: false }"
>
<div class="space-y-3 scroll3">
<p class="bg-[#F7FAFF] px-3 py-1.5 rounded-4px" :style="{color: themeStore.themeColor}">风险优先系数RPN= 严重性S× 可能性P× 可测定性D</p>
<div>
<p class="mb-1 font-13px font-600"> 1风险的严重程度SEV评分制</p>
<NTable size="small" :single-line="false">
<thead>
<tr>
<th class="!text-center w-100px">结果</th>
<th>结果的严重性</th>
<th class="!text-center w-50px">评分</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in fmeaRiskMatrixConfig?.s_grades" :key="index">
<td class="text-center">{{item.label}}</td>
<td>{{item.description}}</td>
<td class="text-center">{{item.score}}</td>
</tr>
</tbody>
</NTable>
</div>
<div>
<p class="mb-1 font-13px font-600"> 2风险的发生几率OCC评分制</p>
<NTable size="small" :single-line="false">
<thead>
<tr>
<th>失败发生的可能性</th>
<th class="!text-center w-50px">评分</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in fmeaRiskMatrixConfig?.p_grades" :key="index">
<td>{{item.label}}{{item.description}}</td>
<td class="text-center">{{item.score}}</td>
</tr>
</tbody>
</NTable>
</div>
<div>
<p class="mb-1 font-13px font-600"> 3风险被检测或发现的可能性DET评分制</p>
<NTable size="small" :single-line="false">
<thead>
<tr>
<th class="!text-center w-100px">发现的可能性</th>
<th>在发生之前通过过程控制可以检测出缺陷的可能性大小</th>
<th class="!text-center w-50px">评分</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in fmeaRiskMatrixConfig?.d_grades" :key="index">
<td class="text-center">{{item.label}}</td>
<td>{{item.description}}</td>
<td class="text-center">{{item.score}}</td>
</tr>
</tbody>
</NTable>
</div>
<div>
<p class="mb-1 font-13px font-600">评价结果</p>
<p class="mb-2">风险评价结果首先遵循<span class="font-600">风险严重程度为高风险的即为高风险</span></p>
<div class="flex flex-col gap-2">
<div v-for="(item,index) in fmeaRiskMatrixConfig?.risk_grades" :key="index" class="flex items-center">
<NTag
size="small"
:color="{
color: item.bg_color,
textColor: item.font_color,
borderColor: item.bg_color,
}"
>
{{item.name}}
</NTag>
<span class="ml-2">{{item.min_rpn}}-{{item.max_rpn}}</span>
<span>{{item.control_measure}}</span>
</div>
</div>
</div>
</div>
</NModal>
</template>
<style scoped lang="scss">
.scroll {
height: calc(100vh - 196px);
overflow-y: auto;
}
.scroll2 {
height: calc(100vh - 160px);
overflow-y: auto;
}
.scroll3 {
max-height: calc(100vh - 140px);
overflow-y: auto;
}
</style>
@@ -1057,7 +1057,7 @@ onBeforeUnmount(() => {
<div v-for="(dev, devIndex) in obj.deviations" :key="devIndex"
@click="clickDev(dev,Number(nodeIndex))"
@contextmenu.prevent="handleContextMenu($event, 'dev', node, obj, dev, Number(nodeIndex), Number(objIndex), Number(devIndex))"
class="cursor-pointer flex w-full items-center ml-2 gap-1.5 rounded-md px-3 py-1.5 text-left text-xs transition"
class="cursor-pointer flex items-center ml-4 gap-1.5 rounded-md px-2 py-1.5 text-left text-xs transition"
:class="currentDev?.id === dev.id ? 'bg-[var(--theme-color)] text-white' : 'text-slate-600 hover:bg-slate-100'"
>
<span class="flex-1">{{ dev.deviation }}</span>