hazop插件提交
This commit is contained in:
@@ -96,3 +96,28 @@ export function hazopRowAddApi(dev_id: number, data: {
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 编辑偏离分析行
|
||||
export function hazopRowEditApi(row_id: number, data: {
|
||||
cause: string,
|
||||
consequence: string,
|
||||
l_value: number,
|
||||
s_value: number,
|
||||
risk_value:string,
|
||||
safeguards:string,
|
||||
suggestions:string
|
||||
}) {
|
||||
return request({
|
||||
url: `/api/hazop/rows/${row_id}`,
|
||||
method: 'put',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除偏离分析行
|
||||
export function hazopRowDelApi(row_id: number) {
|
||||
return request({
|
||||
url: `/api/hazop/rows/${row_id}`,
|
||||
method: 'delete',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useMessage } from "naive-ui";
|
||||
import { Icon } from '@iconify/vue'
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { AiError, aiHazopDeviation } from '@/service/api/ai';
|
||||
import Ed from "./Ed.vue";
|
||||
import { matrixConfigApi } from "@/service/api/system";
|
||||
import {
|
||||
hazopNodesApi,
|
||||
@@ -17,7 +16,9 @@ import {
|
||||
hazopNodeDelApi,
|
||||
hazopObjDelApi,
|
||||
hazopDevDelApi,
|
||||
hazopRowAddApi
|
||||
hazopRowAddApi,
|
||||
hazopRowEditApi,
|
||||
hazopRowDelApi,
|
||||
} from "@/service/api/plugin";
|
||||
|
||||
const themeStore = useThemeStore();
|
||||
@@ -99,6 +100,38 @@ const nodes = ref<any>([]);
|
||||
// 表格样式
|
||||
const TH = "border bg-slate-100 px-2 py-1.5 text-[11px] font-medium text-slate-500";
|
||||
const TD = "border px-1.5 py-1 align-top";
|
||||
// 偏离总数
|
||||
const devTotal = computed(() => {
|
||||
let total = 0;
|
||||
nodes.value.forEach((node:any) => {
|
||||
node.objects.forEach((obj:any) => {
|
||||
total += obj.deviations.length;
|
||||
});
|
||||
});
|
||||
return total;
|
||||
});
|
||||
// 已分析偏离数
|
||||
const devDone = computed(() => {
|
||||
let nonEmptyCount = 0;
|
||||
nodes.value.forEach((node:any) => {
|
||||
node.objects.forEach((obj:any) => {
|
||||
obj.deviations.forEach((dev:any) => {
|
||||
if (dev.rows && dev.rows.length > 0) {
|
||||
nonEmptyCount++;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return nonEmptyCount;
|
||||
});
|
||||
// 已完成记录总数
|
||||
const recTotal = computed(() => {
|
||||
const totalRows = nodes.value.flatMap((node:any) => node.objects)
|
||||
.flatMap((obj:any) => obj.deviations)
|
||||
.filter((d:any) => d.rows && d.rows.length > 0)
|
||||
.reduce((sum:number, d:any) => sum + d.rows.length, 0);
|
||||
return totalRows;
|
||||
});
|
||||
|
||||
|
||||
const HAZOP_AI:any = {
|
||||
@@ -124,9 +157,6 @@ const lowerOf = (lv: "高" | "中" | "低"): "高" | "中" | "低" => {
|
||||
const sel = ref({ n: 0, d: 0 });
|
||||
const analyzing = ref(false);
|
||||
|
||||
const devTotal = computed(() => 0);
|
||||
const devDone = computed(() => 0);
|
||||
const recTotal = computed(() => 0);
|
||||
|
||||
const toRec = (obj: string, dev: string, row: any): any => ({
|
||||
item: `${dev}(${obj})`,
|
||||
@@ -138,12 +168,6 @@ const toRec = (obj: string, dev: string, row: any): any => ({
|
||||
source: "HAZOP",
|
||||
});
|
||||
|
||||
const updRow = (ri: number, patch: Partial<any>) => {
|
||||
const row = currentDev.value.rows[ri];
|
||||
if (row) Object.assign(row, patch);
|
||||
};
|
||||
const clampLS = (v: string) => Math.max(1, Math.min(5, +v || 1));
|
||||
|
||||
const runAi = async () => {
|
||||
if (currentDev.value.rows.length) return message.info("当前偏离已有分析记录,可手动新增行补充");
|
||||
analyzing.value = true;
|
||||
@@ -202,6 +226,21 @@ const selectWorker = (n: string) => {
|
||||
sels.value = sels.value.includes(n) ? sels.value.filter((x) => x !== n) : [...sels.value, n];
|
||||
};
|
||||
|
||||
// 保存
|
||||
const save = () => {
|
||||
window.$message?.success("已手动保存,分析记录数已同步至工具卡片(内容修改实时自动保存,退出不丢失)");
|
||||
};
|
||||
|
||||
// AI整理如表
|
||||
const exportAll = () => {
|
||||
emit("export", nodes.value.flatMap((n) => n.devs.flatMap((d) => d.rows.map((row) => toRec(n.obj, d.name, row)))), "ai");
|
||||
}
|
||||
|
||||
// 导出 Word
|
||||
const exportWord = () => {
|
||||
window.$message?.success('HAZOP 分析报告已导出(Word)')
|
||||
}
|
||||
|
||||
// 根据l和s获取风险等级
|
||||
const riskLevel = (l: number, s: number) => {
|
||||
const num = l * s;
|
||||
@@ -216,8 +255,9 @@ const riskLevel = (l: number, s: number) => {
|
||||
|
||||
// 新增行
|
||||
const addRow = async () => {
|
||||
console.log(l_options.value)
|
||||
console.log(s_options.value)
|
||||
if(!currentDev.value){
|
||||
return window.$message?.warning("请先在左侧选择偏离")
|
||||
}
|
||||
loading.value = true;
|
||||
const {data,error} = await hazopRowAddApi(currentDev.value?.id,{
|
||||
cause: "",
|
||||
@@ -229,35 +269,39 @@ const addRow = async () => {
|
||||
suggestions: "",
|
||||
})
|
||||
if(!error){
|
||||
console.log(data)
|
||||
// getHazopNodes();
|
||||
currentDev.value.rows.push(data);
|
||||
window.$message?.success("新增行成功")
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
// 编辑行
|
||||
const editRow = async (row: any) => {
|
||||
const {error} = await hazopRowEditApi(row.id,{
|
||||
cause: row.cause,
|
||||
consequence: row.consequence,
|
||||
l_value: row.l_value,
|
||||
s_value: row.s_value,
|
||||
risk_value: riskLevel(row.l_value, row.s_value)?.name || '',
|
||||
safeguards: row.safeguards,
|
||||
suggestions: row.suggestions,
|
||||
})
|
||||
}
|
||||
// 删除行
|
||||
const delRow = (index: number) => currentDev.value.rows.splice(index, 1);
|
||||
const delRow = async (index: number, row_id: number) => {
|
||||
loading.value = true;
|
||||
const {error} = await hazopRowDelApi(row_id);
|
||||
if(!error){
|
||||
currentDev.value.rows.splice(index, 1);
|
||||
window.$message?.success("删除成功")
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 展开收起
|
||||
const leftToggle = (id: string) => {
|
||||
collapsed.value = { ...collapsed.value, [id]: !collapsed.value[id] }
|
||||
}
|
||||
|
||||
// 保存
|
||||
const save = () => {
|
||||
window.$message?.success("已手动保存,分析记录数已同步至工具卡片(内容修改实时自动保存,退出不丢失)");
|
||||
};
|
||||
|
||||
// AI整理如表
|
||||
const exportAll = () => {
|
||||
emit("export", nodes.value.flatMap((n) => n.devs.flatMap((d) => d.rows.map((row) => toRec(n.obj, d.name, row)))), "ai");
|
||||
}
|
||||
|
||||
// 导出 Word
|
||||
const exportWord = () => {
|
||||
window.$message?.success('HAZOP 分析报告已导出(Word)')
|
||||
}
|
||||
|
||||
// 打开弹窗
|
||||
const openModal = (type: string,obj: string, title: string,editName: string = '') => {
|
||||
if(type==='add'){
|
||||
@@ -511,6 +555,7 @@ const getHazopNodes = async () => {
|
||||
treeLoading.value = true;
|
||||
const {data,error} = await hazopNodesApi(props.currentId);
|
||||
if(!error){
|
||||
console.log(data)
|
||||
nodes.value = data;
|
||||
}
|
||||
treeLoading.value = false;
|
||||
@@ -660,17 +705,17 @@ onMounted(() => {
|
||||
<thead>
|
||||
<tr>
|
||||
<th :class="TH" class="w-16">操作</th>
|
||||
<th :class="TH">原因</th>
|
||||
<th :class="TH">后果</th>
|
||||
<th :class="TH" class="w-8">L</th>
|
||||
<th :class="TH" class="w-8">S</th>
|
||||
<th :class="TH" class="w-60">原因</th>
|
||||
<th :class="TH" class="w-60">后果</th>
|
||||
<th :class="TH" class="w-6">L</th>
|
||||
<th :class="TH" class="w-6">S</th>
|
||||
<th :class="TH" class="w-20">RR</th>
|
||||
<th :class="TH">安全措施</th>
|
||||
<th :class="TH">建议措施</th>
|
||||
<th :class="TH" class="w-60">安全措施</th>
|
||||
<th :class="TH" class="w-60">建议措施</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="currentDev?.rows?.length === 0">
|
||||
<tr v-if="!currentDev || currentDev?.rows?.length === 0">
|
||||
<td colspan="8" class="px-3 py-8 text-center text-xs text-slate-400">
|
||||
当前偏离暂无分析记录,点击右上角「AI 分析当前偏离」自动生成,或「新增行」手动填写
|
||||
</td>
|
||||
@@ -681,33 +726,53 @@ onMounted(() => {
|
||||
<NButton text type="primary" class="mt-1px text-[11px] hover:underline" title="选入风险分析记录表"
|
||||
@click="message.success('已选入 1 条风险记录,可继续挑选或返回查看记录表')">入表
|
||||
</NButton>
|
||||
<NButton text type="error" title="删除行" @click="delRow(Number(ri))">
|
||||
<Icon icon="ep:delete" class="size-14px" />
|
||||
</NButton>
|
||||
|
||||
<NPopconfirm
|
||||
positive-text="确定"
|
||||
:positiveButtonProps="{ size: 'tiny' }"
|
||||
:negativeButtonProps="{ size: 'tiny' }"
|
||||
@positive-click="delRow(Number(ri), row.id)"
|
||||
>
|
||||
<template #trigger>
|
||||
<NButton text type="error" title="删除行">
|
||||
<Icon icon="ep:delete" class="size-14px" />
|
||||
</NButton>
|
||||
</template>
|
||||
确定删除吗?
|
||||
</NPopconfirm>
|
||||
</div>
|
||||
</td>
|
||||
<td :class="TD">
|
||||
<Ed :v="row.cause" @update="(x) => updRow(Number(ri), { cause: x })" />
|
||||
<NInput type="textarea" size="small" v-model:value="row.cause" :autosize="{minRows:1}" class="text-xs" @blur="editRow(row)" />
|
||||
</td>
|
||||
<td :class="TD">
|
||||
<Ed :v="row.consequence" @update="(x) => updRow(Number(ri), { consequence: x })" />
|
||||
<NInput type="textarea" size="small" v-model:value="row.consequence" :autosize="{minRows:1}" class="text-xs" @blur="editRow(row)" />
|
||||
</td>
|
||||
<td :class="TD" class="text-center">
|
||||
<NInputNumber v-model:value="row.lvalue" size="tiny" :min="l_options[0].key" :max="l_options[l_options.length-1].key" :show-button="false" class="text-center w-10" />
|
||||
<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="editRow(row)" />
|
||||
</td>
|
||||
<td :class="TD" class="text-center">
|
||||
<NInputNumber v-model:value="row.svalue" size="tiny" :min="s_options[0].key" :max="l_options[s_options.length-1].key" :show-button="false" class="text-center w-10" />
|
||||
<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="editRow(row)" />
|
||||
</td>
|
||||
<td :class="TD" class="text-center">
|
||||
<span class="inline-block rounded px-1.5 py-0.5 text-[11px] font-medium">
|
||||
{{ row.risk_value }}
|
||||
</span>
|
||||
<NTag
|
||||
v-if="row.l_value>0 && row.s_value>0"
|
||||
class="text-[11px]"
|
||||
size="small"
|
||||
:color="{
|
||||
color: riskLevel(row.l_value, row.s_value)?.bg_color,
|
||||
textColor: riskLevel(row.l_value, row.s_value)?.font_color,
|
||||
borderColor: riskLevel(row.l_value, row.s_value)?.bg_color,
|
||||
}"
|
||||
>
|
||||
{{riskLevel(row.l_value, row.s_value)?.name}}({{row.l_value * row.s_value}})
|
||||
</NTag>
|
||||
</td>
|
||||
<td :class="TD">
|
||||
<Ed :v="row.safeguards" @update="(x) => updRow(Number(ri), { safeguards: x })" />
|
||||
<NInput type="textarea" size="small" v-model:value="row.safeguards" :autosize="{minRows:1}" class="text-xs" @blur="editRow(row)" />
|
||||
</td>
|
||||
<td :class="TD">
|
||||
<Ed :v="row.suggestions" @update="(x) => updRow(Number(ri), { suggestions: x })" />
|
||||
<NInput type="textarea" size="small" v-model:value="row.suggestions" :autosize="{minRows:1}" class="text-xs" @blur="editRow(row)" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user