风险矩阵配置接口完成
This commit is contained in:
@@ -84,5 +84,13 @@ export function matrixConfigApi(bizType:string) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 保存风险矩阵配置
|
||||||
|
export function saveMatrixConfigApi(bizType:string,data:any) {
|
||||||
|
return request({
|
||||||
|
url: `/api/risk-matrix/${bizType}`,
|
||||||
|
method: 'put',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const form = ref<ApplyForm>({
|
|||||||
name: "", dept: "", mainChanges: [""], related: "", purposes: [], effect: "",
|
name: "", dept: "", mainChanges: [""], related: "", purposes: [], effect: "",
|
||||||
type: "", level: "", duration: "", urgent: false, date: null, restoreDate: null,
|
type: "", level: "", duration: "", urgent: false, date: null, restoreDate: null,
|
||||||
});
|
});
|
||||||
const tab = ref<string>("risk");
|
const tab = ref<string>("pre");
|
||||||
const TABS = [
|
const TABS = [
|
||||||
{ id: "pre", name: "变更预识别", icon: 'lucide:sparkles' },
|
{ id: "pre", name: "变更预识别", icon: 'lucide:sparkles' },
|
||||||
{ id: "form", name: "变更申请表", icon: 'akar-icons:file' },
|
{ id: "form", name: "变更申请表", icon: 'akar-icons:file' },
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
editSystemConfigApi,
|
editSystemConfigApi,
|
||||||
deleteSystemConfigApi,
|
deleteSystemConfigApi,
|
||||||
updateConfigApi,
|
updateConfigApi,
|
||||||
matrixConfigApi
|
matrixConfigApi,
|
||||||
|
saveMatrixConfigApi
|
||||||
} from '@/service/api/system';
|
} from '@/service/api/system';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
@@ -273,6 +274,13 @@ const ruleOptions = ref<{label:string, value:string}[]>([
|
|||||||
{label:'按年度重置',value:'YEARLY'},
|
{label:'按年度重置',value:'YEARLY'},
|
||||||
{label:'连续不重置',value:'CONTINUE'},
|
{label:'连续不重置',value:'CONTINUE'},
|
||||||
])
|
])
|
||||||
|
const levelOptions = ref<{label:string, value:string | null}[]>([
|
||||||
|
{label:'低',value:'低'},
|
||||||
|
{label:'一般',value:'一般'},
|
||||||
|
{label:'较高',value:'较高'},
|
||||||
|
{label:'高',value:'高'},
|
||||||
|
{label:'很高',value:'很高'},
|
||||||
|
])
|
||||||
|
|
||||||
// 保存编号规则
|
// 保存编号规则
|
||||||
const saveRule = async () => {
|
const saveRule = async () => {
|
||||||
@@ -292,82 +300,102 @@ const saveRule = async () => {
|
|||||||
|
|
||||||
// *************** 风险矩阵设置(按分析方法) ***************//
|
// *************** 风险矩阵设置(按分析方法) ***************//
|
||||||
|
|
||||||
|
const matrixLoading = ref<boolean>(false);
|
||||||
const dimensionModal = ref<boolean>(false);
|
const dimensionModal = ref<boolean>(false);
|
||||||
const dimensionLoading = ref<boolean>(false);
|
const dimForm = ref<{s:{key:string, label:string}[], l:{key:string, label:string}[]}>({ s: [], l: [] });
|
||||||
const dimForm = ref<{s:string[], l:string[]}>({ s: [], l: [] });
|
|
||||||
// 默认风险矩阵维度分级定义
|
|
||||||
const matrices = ref([
|
|
||||||
{
|
|
||||||
tool: "HAZOP",
|
|
||||||
sLabels: ["轻微", "一般", "较重", "严重", "灾难"],
|
|
||||||
lLabels: ["极少发生", "较少发生", "偶尔发生", "可能发生", "频繁发生"],
|
|
||||||
levels: [
|
|
||||||
{ name: "低", min: 1, max: 4, bgColor:'#d1fae5', color:'#047857' },
|
|
||||||
{ name: "一般", min: 5, max: 9, bgColor:'#fef9c3', color:'#c27707' },
|
|
||||||
{ name: "较大", min: 10, max: 16, bgColor:'#fee2e2', color:'#c2410c' },
|
|
||||||
{ name: "重大", min: 17, max: 25, bgColor:'#fff3cd', color:'#b91c1c' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tool: "JSA",
|
|
||||||
sLabels: ["轻微伤害", "轻伤", "重伤", "单人死亡", "多人死亡"],
|
|
||||||
lLabels: ["极不可能", "不太可能", "有可能", "较可能", "很可能"],
|
|
||||||
levels: [
|
|
||||||
{ name: "低", min: 1, max: 6, bgColor:'#d1fae5', color:'#047857' },
|
|
||||||
{ name: "一般", min: 7, max: 12, bgColor:'#fef9c3', color:'#c27707' },
|
|
||||||
{ name: "较大", min: 13, max: 19, bgColor:'#fee2e2', color:'#c2410c' },
|
|
||||||
{ name: "重大", min: 20, max: 25, bgColor:'#fff3cd', color:'#b91c1c' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tool: "SCL",
|
|
||||||
sLabels: ["轻微影响", "一般影响", "较大影响", "严重影响", "灾难性影响"],
|
|
||||||
lLabels: ["极少", "较少", "偶尔", "可能", "经常"],
|
|
||||||
levels: [
|
|
||||||
{ name: "低", min: 1, max: 2, bgColor:'#d1fae5', color:'#047857' },
|
|
||||||
{ name: "一般", min: 3, max: 6, bgColor:'#fef9c3', color:'#c27707' },
|
|
||||||
{ name: "较大", min: 7, max: 12, bgColor:'#fee2e2', color:'#c2410c' },
|
|
||||||
{ name: "重大", min: 13, max: 25, bgColor:'#fff3cd', color:'#b91c1c' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
// 当前选中的风险矩阵索引
|
// 当前选中的风险矩阵索引
|
||||||
const activeMatrix = ref(0);
|
const activeMatrix = ref('HAZOP');
|
||||||
// 当前选中的风险矩阵
|
// 当前选中的风险矩阵
|
||||||
const curMatrix = ref<any>(null);
|
const curMatrix = ref<any>(null);
|
||||||
const cellScore = (s:number, l:number) => (s + 1) * (l + 1);
|
// 查找匹配的等级配置
|
||||||
|
function findMatchingConfig(num:number) {
|
||||||
|
for (let i = 0; i < curMatrix.value?.risk_grades.length; i++) {
|
||||||
|
const item = curMatrix.value?.risk_grades[i];
|
||||||
|
if (num >= item.min && num <= item.max) {
|
||||||
|
return item; // 返回整条数据
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 计算单元格等级
|
||||||
|
const cellScore = (s:number, l:number) => {
|
||||||
|
return s*l;
|
||||||
|
}
|
||||||
const cellLevel = (s:number, l:number) => {
|
const cellLevel = (s:number, l:number) => {
|
||||||
const sc = cellScore(s, l);
|
const info = findMatchingConfig(s*l);
|
||||||
return curMatrix.value?.levels.find((lv:{min:number,max:number}) => sc >= lv.min && sc <= lv.max);
|
return info? info:null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 风险矩阵tabs切换
|
// 风险矩阵tabs切换
|
||||||
const handleTabChange = (v: number) => {
|
const handleTabChange = (v: string) => {
|
||||||
activeMatrix.value = v;
|
activeMatrix.value = v;
|
||||||
curMatrix.value = matrices.value[v];
|
matrixConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开风险矩阵编辑弹窗
|
// 打开风险矩阵编辑弹窗
|
||||||
const openDimEdit = () => {
|
const openDimEdit = () => {
|
||||||
dimForm.value = { s: [...curMatrix.value?.sLabels], l: [...curMatrix.value?.lLabels] };
|
dimForm.value = { s: [...curMatrix.value?.consequence], l: [...curMatrix.value?.probability] };
|
||||||
dimensionModal.value = true;
|
dimensionModal.value = true;
|
||||||
}
|
}
|
||||||
// 添加后果严重性 S
|
// 添加后果严重性 S
|
||||||
const addS = () => {
|
const addS = () => {
|
||||||
dimForm.value.s.push("");
|
dimForm.value.s.push({key:'S'+(dimForm.value.s.length+1), label:''});
|
||||||
}
|
}
|
||||||
// 添加发生可能性 L
|
// 添加发生可能性 L
|
||||||
const addL = () => {
|
const addL = () => {
|
||||||
dimForm.value.l.push("");
|
dimForm.value.l.push({key:'L'+(dimForm.value.l.length+1), label:''});
|
||||||
|
}
|
||||||
|
// 删除后果严重性 S
|
||||||
|
const deleteS = (i:number) => {
|
||||||
|
dimForm.value.s.splice(i, 1);
|
||||||
|
}
|
||||||
|
// 删除发生可能性 L
|
||||||
|
const deleteL = (i:number) => {
|
||||||
|
dimForm.value.l.splice(i, 1);
|
||||||
|
}
|
||||||
|
// 添加等级配置
|
||||||
|
const addLevelConfig = () => {
|
||||||
|
curMatrix.value?.risk_grades.push({name:null,bg_color:'#333333',font_color:'#ffffff', min:null, max:null});
|
||||||
|
}
|
||||||
|
// 删除等级配置
|
||||||
|
const delLevelConfig = (index:number) => {
|
||||||
|
curMatrix.value?.risk_grades.splice(index, 1);
|
||||||
}
|
}
|
||||||
// 矩阵维度分级定义保存
|
// 矩阵维度分级定义保存
|
||||||
const dimensionSave = () => {
|
const dimensionSave = () => {
|
||||||
dimensionLoading.value = true;
|
const hasEmptyLabel = dimForm.value.s.some((item:any) => item.label === '');
|
||||||
setTimeout(() => {
|
if(hasEmptyLabel){
|
||||||
window.$message?.success("操作成功");
|
return window.$message?.warning("请完善后果严重性S信息");
|
||||||
dimensionLoading.value = false;
|
}
|
||||||
dimensionModal.value = false;
|
const hasEmptyLabelL = dimForm.value.l.some((item:any) => item.label === '');
|
||||||
}, 1000);
|
if(hasEmptyLabelL){
|
||||||
|
return window.$message?.warning("请完善发生可能性L信息");
|
||||||
|
}
|
||||||
|
curMatrix.value.consequence = [...dimForm.value.s];
|
||||||
|
curMatrix.value.probability = [...dimForm.value.l];
|
||||||
|
dimensionModal.value = false;
|
||||||
|
}
|
||||||
|
// 保存风险矩阵
|
||||||
|
const saveMatrix = async () => {
|
||||||
|
if(curMatrix.value.risk_grades.length === 0){
|
||||||
|
return window.$message?.warning("请添加按分值区间定级信息");
|
||||||
|
}
|
||||||
|
const hasNull = curMatrix.value.risk_grades.some((item:any) =>
|
||||||
|
item.name === null || item.min === null || item.max === null
|
||||||
|
);
|
||||||
|
if(hasNull){
|
||||||
|
return window.$message?.warning("请完善按分值区间定级信息");
|
||||||
|
}
|
||||||
|
matrixLoading.value = true;
|
||||||
|
const {error} = await saveMatrixConfigApi(activeMatrix.value,{
|
||||||
|
consequence:curMatrix.value.consequence,
|
||||||
|
probability:curMatrix.value.probability,
|
||||||
|
risk_grades:curMatrix.value.risk_grades,
|
||||||
|
});
|
||||||
|
if(!error){
|
||||||
|
window.$message?.success("保存成功");
|
||||||
|
}
|
||||||
|
matrixLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取配置
|
// 获取配置
|
||||||
@@ -407,15 +435,13 @@ const getConfig = async () => {
|
|||||||
|
|
||||||
// 获取风险矩阵配置
|
// 获取风险矩阵配置
|
||||||
const matrixConfig = async () => {
|
const matrixConfig = async () => {
|
||||||
const {data,error} = await matrixConfigApi(curMatrix.value?.tool);
|
const {data,error} = await matrixConfigApi(activeMatrix.value);
|
||||||
if(!error){
|
if(!error){
|
||||||
// curMatrix.value = data;
|
curMatrix.value = data;
|
||||||
console.log(data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
curMatrix.value = matrices.value[activeMatrix.value];
|
|
||||||
matrixConfig();
|
matrixConfig();
|
||||||
getConfig();
|
getConfig();
|
||||||
})
|
})
|
||||||
@@ -714,39 +740,42 @@ onMounted(() => {
|
|||||||
<Icon icon="akar-icons:edit" class="size-14px mr-1" />
|
<Icon icon="akar-icons:edit" class="size-14px mr-1" />
|
||||||
维度分级定义
|
维度分级定义
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton size="small" type="primary" class="text-xs">
|
<NButton size="small" type="primary" class="text-xs" :loading="matrixLoading" @click="saveMatrix">
|
||||||
保存矩阵
|
保存矩阵
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="py-3 px-4">
|
<div class="py-3 px-4">
|
||||||
<NTabs type="segment" animated :on-update:value="handleTabChange">
|
<NTabs type="segment" animated :on-update:value="handleTabChange">
|
||||||
<NTabPane :name="0" tab="HAZOP分析矩阵">
|
<NTabPane name="HAZOP" tab="HAZOP分析矩阵">
|
||||||
<div class="flex flex-wrap items-start gap-6">
|
<div class="flex flex-wrap items-start gap-6">
|
||||||
<div class="shrink-0">
|
<div class="shrink-0">
|
||||||
<table class="border-collapse text-center text-[11px]">
|
<table class="border-collapse text-center text-[11px]">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="px-1"></th>
|
<th class="px-1"></th>
|
||||||
<th v-for="(lb, li) in curMatrix?.lLabels" :key="li" class="px-0.5 pb-1 font-normal leading-tight text-slate-500">
|
<th v-for="(lb, li) in curMatrix?.probability" :key="li" class="px-0.5 pb-1 font-normal leading-tight text-slate-500">
|
||||||
L{{ Number(li) + 1 }}<br />{{ lb }}
|
{{ lb.key }}<br />{{ lb.label }}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="row in 5" :key="row">
|
<tr v-for="(row,rowIndex) in curMatrix?.consequence" :key="row.key">
|
||||||
<td class="whitespace-nowrap pr-1.5 text-right leading-tight text-slate-500">S{{ 6 - row }} {{ curMatrix?.sLabels[5 - row] }}</td>
|
<td class="whitespace-nowrap pr-1.5 text-right leading-tight text-slate-500">
|
||||||
<td v-for="col in 5" :key="col" class="p-0.5">
|
{{ curMatrix?.consequence[curMatrix?.consequence.length - (Number(rowIndex)+1)]?.key }}
|
||||||
<div
|
{{ curMatrix?.consequence[curMatrix?.consequence.length - (Number(rowIndex)+1)]?.label }}
|
||||||
class="flex h-9 w-12 items-center justify-center rounded font-semibold"
|
</td>
|
||||||
:style="{
|
<td v-for="(col,colIndex) in curMatrix?.probability" :key="col.key" class="p-0.5">
|
||||||
backgroundColor:cellLevel(5 - row, col - 1)?.bgColor,
|
<div
|
||||||
color:cellLevel(5 - row, col - 1)?.color,
|
class="flex h-9 w-12 items-center justify-center rounded font-semibold"
|
||||||
}"
|
:style="{
|
||||||
>
|
backgroundColor:cellLevel(curMatrix?.consequence.length - Number(rowIndex), (Number(colIndex)+1))?.bg_color,
|
||||||
{{ cellScore(5 - row, col - 1) }}
|
color:cellLevel(curMatrix?.consequence.length - Number(rowIndex), (Number(colIndex)+1))?.font_color,
|
||||||
</div>
|
}"
|
||||||
</td>
|
>
|
||||||
</tr>
|
{{ cellScore(curMatrix?.consequence.length - Number(rowIndex), (Number(colIndex)+1)) }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="mt-1.5 flex justify-between text-[11px] text-slate-400">
|
<div class="mt-1.5 flex justify-between text-[11px] text-slate-400">
|
||||||
@@ -758,15 +787,15 @@ onMounted(() => {
|
|||||||
<!-- 等级分值区间 -->
|
<!-- 等级分值区间 -->
|
||||||
<div class="min-w-72 flex-1">
|
<div class="min-w-72 flex-1">
|
||||||
<div class="mb-2 text-xs text-slate-500 flex items-center gap-2">
|
<div class="mb-2 text-xs text-slate-500 flex items-center gap-2">
|
||||||
<NButton size="tiny" ghost type="primary">
|
<NButton size="tiny" ghost type="primary" @click="addLevelConfig">
|
||||||
<Icon icon="ic:round-plus" class="size-14px" />新增
|
<Icon icon="ic:round-plus" class="size-14px" />新增
|
||||||
</NButton>
|
</NButton>
|
||||||
风险值 = 严重性 S × 可能性 L,按分值区间定级:
|
风险值 = 严重性 S × 可能性 L,按分值区间定级:
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<div v-for="lv in curMatrix?.levels" :key="lv.name" class="flex items-center gap-2 rounded-lg border px-2.5 py-2">
|
<div v-for="(lv,index) in curMatrix?.risk_grades" :key="lv.name" class="flex items-center gap-2 rounded-lg border px-2.5 py-2">
|
||||||
<span class="text-xs">背景</span>
|
<span class="text-xs">背景</span>
|
||||||
<NColorPicker v-model:value="lv.bgColor">
|
<NColorPicker v-model:value="lv.bg_color">
|
||||||
<template #trigger="{ value, onClick, ref: triggerRef }">
|
<template #trigger="{ value, onClick, ref: triggerRef }">
|
||||||
<div
|
<div
|
||||||
:ref="triggerRef"
|
:ref="triggerRef"
|
||||||
@@ -780,7 +809,7 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</NColorPicker>
|
</NColorPicker>
|
||||||
<span class="text-xs">字体</span>
|
<span class="text-xs">字体</span>
|
||||||
<NColorPicker v-model:value="lv.color">
|
<NColorPicker v-model:value="lv.font_color">
|
||||||
<template #trigger="{ value, onClick, ref: triggerRef }">
|
<template #trigger="{ value, onClick, ref: triggerRef }">
|
||||||
<div
|
<div
|
||||||
:ref="triggerRef"
|
:ref="triggerRef"
|
||||||
@@ -793,19 +822,130 @@ onMounted(() => {
|
|||||||
></div>
|
></div>
|
||||||
</template>
|
</template>
|
||||||
</NColorPicker>
|
</NColorPicker>
|
||||||
<NInput v-model:value="lv.name" size="small" class="!w-16 text-xs" />
|
<NSelect v-model:value="lv.name" :options="levelOptions" size="small" class="!w-20 text-xs" />
|
||||||
<span class="shrink-0 text-xs text-slate-400">分值</span>
|
<span class="shrink-0 text-xs text-slate-400">分值</span>
|
||||||
<NInputNumber v-model:value="lv.min" :min="1" :max="25" size="small" :show-button="false" class="!w-14 text-center text-xs" />
|
<NInputNumber v-model:value="lv.min" :min="1" size="small" :show-button="false" class="!w-16 text-center text-xs" />
|
||||||
<span class="text-slate-400">–</span>
|
<span class="text-slate-400">–</span>
|
||||||
<NInputNumber v-model:value="lv.max" :min="1" :max="25" size="small" :show-button="false" class="!w-14 text-center text-xs" />
|
<NInputNumber v-model:value="lv.max" :min="1" size="small" :show-button="false" class="!w-16 text-center text-xs" />
|
||||||
<NTag
|
<NTag
|
||||||
|
v-if="lv.name"
|
||||||
size="small"
|
size="small"
|
||||||
:color="{color: lv.bgColor, textColor: lv.color, borderColor: lv.bgColor}"
|
:color="{color: lv.bg_color, textColor: lv.font_color, borderColor: lv.bg_color}"
|
||||||
class="ml-auto"
|
class="ml-auto"
|
||||||
>
|
>
|
||||||
{{ lv.name }}风险
|
{{ lv.name }}风险
|
||||||
</NTag>
|
</NTag>
|
||||||
<NButton text type="error">
|
<NPopconfirm
|
||||||
|
positive-text="确定"
|
||||||
|
:positiveButtonProps="{ size: 'tiny' }"
|
||||||
|
:negativeButtonProps="{ size: 'tiny' }"
|
||||||
|
@positive-click="delLevelConfig(Number(index))"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<NButton text type="error" :class="lv.name?'':'ml-auto'">
|
||||||
|
<Icon icon="akar-icons:circle-minus" class="size-16px" />
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
确定删除吗?
|
||||||
|
</NPopconfirm>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="mt-2.5 text-[11px] text-slate-400">
|
||||||
|
各分析方法独立维护矩阵与维度分级;风险分析记录表按所选工具对应的矩阵自动定级。分值区间不得重叠、须落在 1–25 内;在途分析记录按原矩阵保留定级快照。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</NTabPane>
|
||||||
|
<NTabPane name="JSA" tab="JSA分析矩阵">
|
||||||
|
<div class="flex flex-wrap items-start gap-6">
|
||||||
|
<div class="shrink-0">
|
||||||
|
<table class="border-collapse text-center text-[11px]">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="px-1"></th>
|
||||||
|
<th v-for="(lb, li) in curMatrix?.probability" :key="li" class="px-0.5 pb-1 font-normal leading-tight text-slate-500">
|
||||||
|
{{ lb.key }}<br />{{ lb.label }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(row,rowIndex) in curMatrix?.consequence" :key="row.key">
|
||||||
|
<td class="whitespace-nowrap pr-1.5 text-right leading-tight text-slate-500">
|
||||||
|
{{ curMatrix?.consequence[curMatrix?.consequence.length - (Number(rowIndex)+1)]?.key }}
|
||||||
|
{{ curMatrix?.consequence[curMatrix?.consequence.length - (Number(rowIndex)+1)]?.label }}
|
||||||
|
</td>
|
||||||
|
<td v-for="(col,colIndex) in curMatrix?.probability" :key="col.key" class="p-0.5">
|
||||||
|
<div
|
||||||
|
class="flex h-9 w-12 items-center justify-center rounded font-semibold"
|
||||||
|
:style="{
|
||||||
|
backgroundColor:cellLevel(curMatrix?.consequence.length - Number(rowIndex), (Number(colIndex)+1))?.bg_color,
|
||||||
|
color:cellLevel(curMatrix?.consequence.length - Number(rowIndex), (Number(colIndex)+1))?.font_color,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ cellScore(curMatrix?.consequence.length - Number(rowIndex), (Number(colIndex)+1)) }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="mt-1.5 flex justify-between text-[11px] text-slate-400">
|
||||||
|
<span>纵轴:后果严重性 S</span>
|
||||||
|
<span>横轴:发生可能性 L</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 等级分值区间 -->
|
||||||
|
<div class="min-w-72 flex-1">
|
||||||
|
<div class="mb-2 text-xs text-slate-500 flex items-center gap-2">
|
||||||
|
<NButton size="tiny" ghost type="primary" @click="addLevelConfig">
|
||||||
|
<Icon icon="ic:round-plus" class="size-14px" />新增
|
||||||
|
</NButton>
|
||||||
|
风险值 = 严重性 S × 可能性 L,按分值区间定级:
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div v-for="(lv,index) in curMatrix?.risk_grades" :key="lv.name" class="flex items-center gap-2 rounded-lg border px-2.5 py-2">
|
||||||
|
<span class="text-xs">背景</span>
|
||||||
|
<NColorPicker v-model:value="lv.bg_color">
|
||||||
|
<template #trigger="{ value, onClick, ref: triggerRef }">
|
||||||
|
<div
|
||||||
|
:ref="triggerRef"
|
||||||
|
class="h-4 w-4 rounded-sm border cursor-pointer"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: value || '#000',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}"
|
||||||
|
@click="onClick"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
</NColorPicker>
|
||||||
|
<span class="text-xs">字体</span>
|
||||||
|
<NColorPicker v-model:value="lv.font_color">
|
||||||
|
<template #trigger="{ value, onClick, ref: triggerRef }">
|
||||||
|
<div
|
||||||
|
:ref="triggerRef"
|
||||||
|
class="h-4 w-4 rounded-sm border cursor-pointer"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: value || '#000',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}"
|
||||||
|
@click="onClick"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
</NColorPicker>
|
||||||
|
<NSelect v-model:value="lv.name" :options="levelOptions" size="small" class="!w-20 text-xs" />
|
||||||
|
<span class="shrink-0 text-xs text-slate-400">分值</span>
|
||||||
|
<NInputNumber v-model:value="lv.min" :min="1" size="small" :show-button="false" class="!w-16 text-center text-xs" />
|
||||||
|
<span class="text-slate-400">–</span>
|
||||||
|
<NInputNumber v-model:value="lv.max" :min="1" size="small" :show-button="false" class="!w-16 text-center text-xs" />
|
||||||
|
<NTag
|
||||||
|
v-if="lv.name"
|
||||||
|
size="small"
|
||||||
|
:color="{color: lv.bg_color, textColor: lv.font_color, borderColor: lv.bg_color}"
|
||||||
|
class="ml-auto"
|
||||||
|
>
|
||||||
|
{{ lv.name }}风险
|
||||||
|
</NTag>
|
||||||
|
<NButton text type="error" :class="lv.name?'':'ml-auto'" @click="delLevelConfig(Number(index))">
|
||||||
<Icon icon="akar-icons:circle-minus" class="size-16px" />
|
<Icon icon="akar-icons:circle-minus" class="size-16px" />
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -816,104 +956,6 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NTabPane>
|
</NTabPane>
|
||||||
<NTabPane :name="1" tab="JSA分析矩阵">
|
|
||||||
<div class="flex flex-wrap items-start gap-6">
|
|
||||||
<div class="shrink-0">
|
|
||||||
<table class="border-collapse text-center text-[11px]">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="px-1"></th>
|
|
||||||
<th v-for="(lb, li) in curMatrix?.lLabels" :key="li" class="px-0.5 pb-1 font-normal leading-tight text-slate-500">
|
|
||||||
L{{ li + 1 }}<br />{{ lb }}
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="row in 5" :key="row">
|
|
||||||
<td class="whitespace-nowrap pr-1.5 text-right leading-tight text-slate-500">S{{ 6 - row }} {{ curMatrix?.sLabels[5 - row] }}</td>
|
|
||||||
<td v-for="col in 5" :key="col" class="p-0.5">
|
|
||||||
<div class="flex h-9 w-12 items-center justify-center rounded border font-semibold" :class="cellLevel(5 - row, col - 1)?.cls">
|
|
||||||
{{ cellScore(5 - row, col - 1) }}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="mt-1.5 flex justify-between text-[11px] text-slate-400">
|
|
||||||
<span>纵轴:后果严重性 S</span>
|
|
||||||
<span>横轴:发生可能性 L</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 等级分值区间 -->
|
|
||||||
<div class="min-w-72 flex-1">
|
|
||||||
<div class="mb-2 text-xs text-slate-500">风险值 = 严重性 S × 可能性 L(5×5),按分值区间定级:</div>
|
|
||||||
<div class="space-y-2">
|
|
||||||
<div v-for="lv in curMatrix?.levels" :key="lv.name" class="flex items-center gap-2 rounded-lg border px-2.5 py-2">
|
|
||||||
<span class="h-3 w-3 shrink-0 rounded-sm border" :class="lv.cls"></span>
|
|
||||||
<NInput v-model:value="lv.name" size="small" class="!w-16 text-xs" />
|
|
||||||
<span class="shrink-0 text-xs text-slate-400">分值</span>
|
|
||||||
<NInputNumber v-model:value="lv.min" :min="1" :max="25" size="small" :show-button="false" class="!w-14 text-center text-xs" />
|
|
||||||
<span class="text-slate-400">–</span>
|
|
||||||
<NInputNumber v-model:value="lv.max" :min="1" :max="25" size="small" :show-button="false" class="!w-14 text-center text-xs" />
|
|
||||||
<NTag size="small" :class="lv.cls" class="ml-auto">{{ lv.name }}风险</NTag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="mt-2.5 text-[11px] text-slate-400">
|
|
||||||
各分析方法独立维护矩阵与维度分级;风险分析记录表按所选工具对应的矩阵自动定级。分值区间不得重叠、须落在 1–25 内;在途分析记录按原矩阵保留定级快照。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</NTabPane>
|
|
||||||
<NTabPane :name="2" tab="SCL分析矩阵">
|
|
||||||
<div class="flex flex-wrap items-start gap-6">
|
|
||||||
<div class="shrink-0">
|
|
||||||
<table class="border-collapse text-center text-[11px]">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="px-1"></th>
|
|
||||||
<th v-for="(lb, li) in curMatrix?.lLabels" :key="li" class="px-0.5 pb-1 font-normal leading-tight text-slate-500">
|
|
||||||
L{{ li + 1 }}<br />{{ lb }}
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="row in 5" :key="row">
|
|
||||||
<td class="whitespace-nowrap pr-1.5 text-right leading-tight text-slate-500">S{{ 6 - row }} {{ curMatrix?.sLabels[5 - row] }}</td>
|
|
||||||
<td v-for="col in 5" :key="col" class="p-0.5">
|
|
||||||
<div class="flex h-9 w-12 items-center justify-center rounded border font-semibold" :class="cellLevel(5 - row, col - 1)?.cls">
|
|
||||||
{{ cellScore(5 - row, col - 1) }}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="mt-1.5 flex justify-between text-[11px] text-slate-400">
|
|
||||||
<span>纵轴:后果严重性 S</span>
|
|
||||||
<span>横轴:发生可能性 L</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 等级分值区间 -->
|
|
||||||
<div class="min-w-72 flex-1">
|
|
||||||
<div class="mb-2 text-xs text-slate-500">风险值 = 严重性 S × 可能性 L(5×5),按分值区间定级:</div>
|
|
||||||
<div class="space-y-2">
|
|
||||||
<div v-for="lv in curMatrix?.levels" :key="lv.name" class="flex items-center gap-2 rounded-lg border px-2.5 py-2">
|
|
||||||
<span class="h-3 w-3 shrink-0 rounded-sm border" :class="lv.cls"></span>
|
|
||||||
<NInput v-model:value="lv.name" size="small" class="!w-16 text-xs" />
|
|
||||||
<span class="shrink-0 text-xs text-slate-400">分值</span>
|
|
||||||
<NInputNumber v-model:value="lv.min" :min="1" :max="25" size="small" :show-button="false" class="!w-14 text-center text-xs" />
|
|
||||||
<span class="text-slate-400">–</span>
|
|
||||||
<NInputNumber v-model:value="lv.max" :min="1" :max="25" size="small" :show-button="false" class="!w-14 text-center text-xs" />
|
|
||||||
<NTag size="small" :class="lv.cls" class="ml-auto">{{ lv.name }}风险</NTag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="mt-2.5 text-[11px] text-slate-400">
|
|
||||||
各分析方法独立维护矩阵与维度分级;风险分析记录表按所选工具对应的矩阵自动定级。分值区间不得重叠、须落在 1–25 内;在途分析记录按原矩阵保留定级快照。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</NTabPane>
|
|
||||||
</NTabs>
|
</NTabs>
|
||||||
</div>
|
</div>
|
||||||
</NCard>
|
</NCard>
|
||||||
@@ -1092,39 +1134,50 @@ onMounted(() => {
|
|||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-1.5 text-xs font-semibold text-slate-600 flex items-center">
|
<div class="mb-1.5 text-xs font-semibold text-slate-600 flex items-center">
|
||||||
后果严重性 S(S1 → S5 递增)
|
后果严重性 S({{dimForm.s[0].key}} → {{dimForm.s[dimForm.s.length-1].key}} 递增)
|
||||||
<NButton size="tiny" ghost type="primary" @click="addS">
|
<NButton size="tiny" ghost type="primary" @click="addS">
|
||||||
<Icon icon="ic:round-plus" class="size-14px" />新增
|
<Icon icon="ic:round-plus" class="size-14px" />新增
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-5 gap-1.5 mt-3">
|
<div class="grid grid-cols-5 gap-1.5 mt-3">
|
||||||
<label v-for="(_, i) in dimForm.s" :key="'s' + i" class="text-center text-xs text-slate-400">
|
<label v-for="(item, i) in dimForm.s" :key="'s' + i" class="text-center text-xs text-slate-400">
|
||||||
<div class="flex items-center justify-center gap-2 mb-1">
|
<div class="flex items-center justify-center gap-2 mb-1">
|
||||||
S{{ i + 1 }}
|
{{ item.key }}
|
||||||
<NButton text type="error">
|
<NPopconfirm
|
||||||
<Icon icon="akar-icons:circle-minus" class="size-16px" />
|
v-if="dimForm.s.length > 5"
|
||||||
</NButton>
|
positive-text="确定"
|
||||||
|
:positiveButtonProps="{ size: 'tiny' }"
|
||||||
|
:negativeButtonProps="{ size: 'tiny' }"
|
||||||
|
@positive-click="deleteS(i)"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<NButton text type="error">
|
||||||
|
<Icon icon="akar-icons:circle-minus" class="size-16px" />
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
确定删除吗?
|
||||||
|
</NPopconfirm>
|
||||||
</div>
|
</div>
|
||||||
<NInput v-model:value="dimForm.s[i]" size="small" class="mt-0.5 text-center text-xs" />
|
<NInput v-model:value="item.label" size="small" class="mt-0.5 text-center text-xs" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-1.5 text-xs font-semibold text-slate-600 flex items-center">
|
<div class="mb-1.5 text-xs font-semibold text-slate-600 flex items-center">
|
||||||
发生可能性 L(L1 → L5 递增)
|
发生可能性 L({{dimForm.l[0].key}} → {{dimForm.l[dimForm.l.length-1].key}} 递增)
|
||||||
<NButton size="tiny" ghost type="primary" @click="addL">
|
<NButton size="tiny" ghost type="primary" @click="addL">
|
||||||
<Icon icon="ic:round-plus" class="size-14px" />新增
|
<Icon icon="ic:round-plus" class="size-14px" />新增
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-5 gap-1.5 mt-3">
|
<div class="grid grid-cols-5 gap-1.5 mt-3">
|
||||||
<label v-for="(_, i) in dimForm.l" :key="'l' + i" class="text-center text-xs text-slate-400">
|
<label v-for="(item, i) in dimForm.l" :key="'l' + i" class="text-center text-xs text-slate-400">
|
||||||
<div class="flex items-center justify-center gap-2 mb-1">
|
<div class="flex items-center justify-center gap-2 mb-1">
|
||||||
L{{ i + 1 }}
|
{{ item.key }}
|
||||||
<NButton text type="error">
|
<NButton v-if="dimForm.l.length > 5" text type="error" @click="deleteL(i)">
|
||||||
<Icon icon="akar-icons:circle-minus" class="size-16px" />
|
<Icon icon="akar-icons:circle-minus" class="size-16px" />
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<NInput v-model:value="dimForm.l[i]" size="small" class="mt-0.5 text-center text-xs" />
|
<NInput v-model:value="item.label" size="small" class="mt-0.5 text-center text-xs" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1132,7 +1185,7 @@ onMounted(() => {
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex justify-end gap-[10px]">
|
<div class="flex justify-end gap-[10px]">
|
||||||
<NButton size="small" @click="dimensionModal = false">取消</NButton>
|
<NButton size="small" @click="dimensionModal = false">取消</NButton>
|
||||||
<NButton size="small" type="primary" :loading="dimensionLoading" @click="dimensionSave">确定</NButton>
|
<NButton size="small" type="primary" @click="dimensionSave">确定</NButton>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</NModal>
|
</NModal>
|
||||||
|
|||||||
Reference in New Issue
Block a user