diff --git a/src/service/api/plugin.ts b/src/service/api/plugin.ts index 8c168ab..c2f9b0c 100644 --- a/src/service/api/plugin.ts +++ b/src/service/api/plugin.ts @@ -9,115 +9,27 @@ export function hazopNodesApi(id: number) { method: 'get', }); } - -// 新增HAZOP节点 -export function hazopNodeAddApi(id: number, data: { node_name: string }) { +// HAZOP保存 +export function hazopSaveApi(change_id: number, data: any) { return request({ - url: `/api/changes/${id}/hazop/nodes`, - method: 'post', - data, - }); -} -// 编辑HAZOP节点 -export function hazopNodeEditApi(node_id: number, data: { node_name: string }) { - return request({ - url: `/api/hazop/nodes/${node_id}`, - method: 'put', - data, - }); -} -// 删除HAZOP节点 -export function hazopNodeDelApi(node_id: number) { - return request({ - url: `/api/hazop/nodes/${node_id}`, - method: 'delete', - }); -} -// 新增分析对象 -export function hazopObjAddApi(node_id: number, data: { obj_name: string }) { - return request({ - url: `/api/hazop/nodes/${node_id}/objects`, - method: 'post', - data, - }); -} -// 编辑分析对象 -export function hazopObjEditApi(obj_id: number, data: { obj_name: string }) { - return request({ - url: `/api/hazop/objects/${obj_id}`, - method: 'put', - data, - }); -} -// 删除分析对象 -export function hazopObjDelApi(obj_id: number) { - return request({ - url: `/api/hazop/objects/${obj_id}`, - method: 'delete', - }); -} -// 新增偏离 -export function hazopDevAddApi(obj_id: number, data: { deviation: string }) { - return request({ - url: `/api/hazop/objects/${obj_id}/deviations`, - method: 'post', - data, - }); -} -// 编辑偏离 -export function hazopDevEditApi(dev_id: number, data: { deviation: string }) { - return request({ - url: `/api/hazop/deviations/${dev_id}`, - method: 'put', - data, - }); -} -// 删除偏离 -export function hazopDevDelApi(dev_id: number) { - return request({ - url: `/api/hazop/deviations/${dev_id}`, - method: 'delete', - }); -} - -// 新增偏离分析行 -export function hazopRowAddApi(dev_id: number, data: { - cause: string, - consequence: string, - l_value: number, - s_value: number, - risk_value:string, - safeguards:[], - suggestions:[] -}) { - return request({ - url: `/api/hazop/deviations/${dev_id}/rows`, - method: 'post', - data, - }); -} - -// 编辑偏离分析行 -export function hazopRowEditApi(row_id: number, data: { - cause: string, - consequence: string, - l_value: number, - s_value: number, - risk_value:string, - safeguards:[], - suggestions:[] -}) { - return request({ - url: `/api/hazop/rows/${row_id}`, + url: `/api/changes/${change_id}/hazop`, method: 'put', data, }); } -// 删除偏离分析行 -export function hazopRowDelApi(row_id: number) { +// 获取JSA详情 +export function jsaDetailApi(change_id: number) { return request({ - url: `/api/hazop/rows/${row_id}`, - method: 'delete', + url: `/api/changes/${change_id}/jsa`, + method: 'get', + }); +} +// JSA保存 +export function jsaSaveApi(change_id: number, data: any) { + return request({ + url: `/api/changes/${change_id}/jsa`, + method: 'put', + data, }); } diff --git a/src/theme/settings.ts b/src/theme/settings.ts index 71a329c..94b8827 100644 --- a/src/theme/settings.ts +++ b/src/theme/settings.ts @@ -5,7 +5,7 @@ export const themeSettings: App.Theme.ThemeSetting = { colourWeakness: false, recommendColor: false, themeColor: '#2080f0', - themeRadius: 6, + themeRadius: 4, otherColor: { info: '#2080f0', success: '#52c41a', diff --git a/src/views/my/initiateChange/index.vue b/src/views/my/initiateChange/index.vue index 450c8c8..df0fbce 100644 --- a/src/views/my/initiateChange/index.vue +++ b/src/views/my/initiateChange/index.vue @@ -176,19 +176,20 @@ const getOrgList = async () => { const getAllFormData = () => { let changePreForm = changePreRef.value?.form; let changeApplyForm = changeApplyRef.value?.form; - let changeRiskForm = riskAnalysisRef.value?.form; + let changeRiskForm = riskAnalysisRef.value?.riskRecords; let changeTrainingForm = changeTrainingRef.value?.form; let pssrForm = pssrRef.value?.list; let evaluationForm = evaluationRef.value?.list; // let closeConfirmForm = closeConfirmRef.value?.form; return { title: title.value, - ...changePreForm, - ...changeApplyForm, - ...changeRiskForm, - ...changeTrainingForm, - ...pssrForm, - ...evaluationForm, + precheck:{...changePreForm}, + apply_form:{...changeApplyForm}, + approval_flow: {}, + risk_records:changeRiskForm, + train:{...changeTrainingForm}, + pssr:{items:pssrForm}, + accept_form:{...evaluationForm}, }; } diff --git a/src/views/my/initiateChange/modules/hazop.vue b/src/views/my/initiateChange/modules/hazop.vue index e2debf8..18304c7 100644 --- a/src/views/my/initiateChange/modules/hazop.vue +++ b/src/views/my/initiateChange/modules/hazop.vue @@ -2,35 +2,18 @@ import { computed, onBeforeUnmount, onMounted, ref } from "vue"; import { Icon } from '@iconify/vue' import { useThemeStore } from '@/store/modules/theme'; +import { useDialog } from 'naive-ui'; import { peopleListApi } from '@/service/api/system'; -import { - hazopNodesApi, - hazopNodeAddApi, - hazopObjAddApi, - hazopDevAddApi, - hazopNodeEditApi, - hazopObjEditApi, - hazopDevEditApi, - hazopNodeDelApi, - hazopObjDelApi, - hazopDevDelApi, - hazopRowAddApi, - hazopRowEditApi, - hazopRowDelApi, -} from "@/service/api/plugin"; +import { hazopNodesApi, hazopSaveApi } from "@/service/api/plugin"; const themeStore = useThemeStore(); +const dialog = useDialog(); const props = defineProps(['isAiOpen','currentId','hazopRiskMatrixConfig']) const emit = defineEmits(['close','toTable']); const loading = ref(false); -const treeLoading = ref(false); const analyzing = ref(false); -// 删除确认弹窗 -const delModal = ref(false); -// 删除确定状态 -const delLoading = ref(false); // 新增/编辑弹窗 const editModal = ref(false); // 当前弹框标题 @@ -39,8 +22,6 @@ const currentModalTitle = ref(''); const currentModalType = ref(''); // 当前弹框对象 const currentModalObj = ref(''); -// 弹窗保存状态 -const modalSaving = ref(false); const name = ref(''); const selectOptions = computed(() => { @@ -74,11 +55,14 @@ const contextMenuShow = ref(false) const contextMenuX = ref(0) const contextMenuY = ref(0) // 存储当前右键点击的目标数据 -const contextTarget = ref<{type: string, node: any, obj: any, dev: any}>({ +const contextTarget = ref<{type: string, node: any, obj: any, dev: any, nodeIndex: number, objIndex: number, devIndex: number}>({ type: '', // 'node' | 'obj' | 'dev' node: null, obj: null, dev: null, + nodeIndex: -1, + objIndex: -1, + devIndex: -1, }) // 当前节点 const currentNode = computed(() => { @@ -142,61 +126,66 @@ const recTotal = computed(() => { return totalRows; }); -const workers = ref<{id:number,name:string,org:string}[]>([]); +// 人员列表源数据 +const workers = ref<{user_id:number,user_name:string,org_name?:string}[]>([]); // 选中的人员 -const selectedWorkers = ref([]); +const selectedWorkers = ref<{user_id:number,user_name:string,org_name?:string}[]>([]); // 选择分析人员弹窗 const openSelectWorker = ref(false); // 搜索分析人员 const searchWorker = ref(""); // 分析人员列表 -const workerList = computed(() => workers.value.filter((x:{id:number,name:string,org:string}) => !searchWorker.value || x.name.includes(searchWorker.value))); - +const workerList = computed(() => workers.value.filter((x:{user_id:number,user_name:string,org_name?:string}) => !searchWorker.value || x.user_name.includes(searchWorker.value))); // 选择分析人员 -const selectWorker = (n: number) => { - selectedWorkers.value = selectedWorkers.value.includes(n) ? selectedWorkers.value.filter((x:number) => x !== n) : [...selectedWorkers.value, n]; +const selectWorker = (n: {user_id:number,user_name:string,org_name?:string}) => { + selectedWorkers.value = selectedWorkers.value.includes(n) ? selectedWorkers.value.filter((x:any) => x.user_id !== n.user_id) : [...selectedWorkers.value, n]; +}; +// 匹配是否有人员 +const hasWorker = (id:number) => { + return selectedWorkers.value.some((item:{user_id:number,user_name:string,org_name?:string}) => item.user_id === id); }; // 保存 -const save = () => { - window.$message?.success("已手动保存,分析记录数已同步至工具卡片(内容修改实时自动保存,退出不丢失)"); +const save = async (isClick: boolean = false) => { + loading.value = isClick; + const workers = (selectedWorkers.value ?? []).map((item: { org_name?:string; user_id: number; user_name: string }) => { + const { org_name, ...rest } = item; + return rest; + }); + const {error} = await hazopSaveApi(props.currentId, { + nodes: nodes.value, + users: workers, + }); + if(!error){ + if(isClick){ + window.$message?.success("保存成功"); + } + } + loading.value = false; }; /** - * 根据行 ID 查找并返回带有父级名称的行对象 - * @param {Array} data - 原始 JSON 数据数组 - * @param {Number|String} rowId - 要查找的行 ID - * @returns {Object|null} - 增强后的行对象,或 null(未找到) + * 根据行下标 查找并返回带有父级名称的行对象 */ -const findRowWithParents = (data:any, rowId:any) => { - for (const node of data) { - const node_ame = node.node_name; - for (const obj of node.objects) { - const obj_ame = obj.obj_name; - for (const dev of obj.deviations) { - const deviation = dev.deviation; - for (const row of dev.rows) { - if (row.id === rowId) { - return { - ...row, - scene: `${row.cause?row.cause+';':''}${deviation?deviation+';':''}${row.consequence?'可能导致:'+row.consequence:''}`, - source: 'HAZOP', - risk_item: `${deviation}${node_ame?`(${node_ame}${obj_ame?`-${obj_ame}`:''})`:''}`, - safe: row?.safeguards?.map((item:any) => item.name).join(';') || '', - suggestion: row?.suggestions?.map((item:any) => item.name).join(';') || '', - to_pssr: 0 - }; - } - } - } - } +const findRowWithParents = (rowIndex: number = -1) => { + const row = currentDev.value.rows[rowIndex]; + if(row){ + return { + ...row, + scene: `${row.cause?row.cause+';':''}${currentDev.value?.deviation+';'}${row.consequence?'可能导致:'+row.consequence:''}`, + source: 'HAZOP', + risk_item: `${currentDev.value?.deviation}${currentNode.value?.node_name?`(${currentNode.value?.node_name}${currentObj.value?.obj_name?`-${currentObj.value?.obj_name}`:''})`:''}`, + safe: row?.safeguards?.map((item:any) => item.name).join(';') || '', + suggestion: row?.suggestions?.map((item:any) => item.name).join(';') || '', + to_pssr: 0 + }; } - return null; // 未找到 + return null; } // AI整理如表、入表 -const enterToTable = (type: string, row: any) => { +const enterToTable = (type: string, rowIndex: number = -1) => { if(type==='single'){ - const enrichedRow = findRowWithParents(nodes.value, row.id); + const enrichedRow = findRowWithParents(rowIndex); emit('toTable',type,enrichedRow?[enrichedRow]:[]); } if(type==='all'){ @@ -283,11 +272,12 @@ const addSafeguard = (index: number) => { }else{ currentDev.value.rows[index].safeguards = [{name:'',value:''}]; } + save() } // 删除安全措施 -const delSafeguard = (index: number, safeguardIndex: number, row: any) => { +const delSafeguard = (index: number, safeguardIndex: number) => { currentDev.value.rows[index].safeguards.splice(safeguardIndex, 1); - editRow(row) + save() } // 新增建议措施 const addSuggestion = (index: number) => { @@ -296,11 +286,12 @@ const addSuggestion = (index: number) => { }else{ currentDev.value.rows[index].suggestions = [{name:'',value:''}]; } + save() } // 删除建议措施 -const delSuggestion = (index: number, suggestionIndex: number, row: any) => { +const delSuggestion = (index: number, suggestionIndex: number) => { currentDev.value.rows[index].suggestions.splice(suggestionIndex, 1); - editRow(row) + save() } // 新增行 @@ -308,8 +299,7 @@ const addRow = async () => { if(!currentDev.value){ return window.$message?.warning("请先在左侧选择偏离") } - loading.value = true; - const {data,error} = await hazopRowAddApi(currentDev.value?.id,{ + currentDev.value.rows.push({ cause: "", consequence: "", l_value: l_options.value[0]?.key || 1, @@ -317,33 +307,12 @@ const addRow = async () => { risk_value: riskLevel(l_options.value[0]?.key || 1, s_options.value[0]?.key || 1)?.name || '', safeguards: [], suggestions: [], - }) - if(!error){ - currentDev.value.rows.push(data); - } - 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 = 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 delRow = async (index: number) => { + currentDev.value.rows.splice(index, 1); + save() } // 展开收起 @@ -369,101 +338,102 @@ const modalSave = async () => { if(name.value===''){ return window.$message?.warning("名称不能为空") } - modalSaving.value = true; // 新增节点 if(currentModalType.value==='add' && currentModalObj.value==='node'){ - const {error} = await hazopNodeAddApi(props.currentId,{node_name: name.value}); - if(!error){ - getHazopNodes(); - window.$message?.success("新增节点成功") - editModal.value = false; - } + nodes.value.push({id:0,node_name:name.value,change_id:props.currentId,objects:[]}); + window.$message?.success("新增节点成功") + editModal.value = false; } // 编辑节点 if(currentModalType.value==='edit' && currentModalObj.value==='node'){ - const {error} = await hazopNodeEditApi(contextTarget.value?.node?.id,{node_name: name.value}); - if(!error){ - getHazopNodes(); - window.$message?.success("编辑节点成功") - editModal.value = false; - } + nodes.value[contextTarget.value?.nodeIndex].node_name = name.value; + window.$message?.success("编辑节点成功") + editModal.value = false; } // 新增分析对象 if(currentModalType.value==='add' && currentModalObj.value==='obj'){ - const {error} = await hazopObjAddApi(contextTarget.value?.node?.id,{obj_name: name.value}); - if(!error){ - getHazopNodes(); - window.$message?.success("新增分析对象成功") - editModal.value = false; + let obj = { + id: 0, + obj_name: name.value, + node_id: nodes.value[contextTarget.value?.nodeIndex]?.id, + deviations: [] } + if(nodes.value[contextTarget.value?.nodeIndex].objects){ + nodes.value[contextTarget.value?.nodeIndex].objects.push(obj); + }else{ + nodes.value[contextTarget.value?.nodeIndex].objects = [obj]; + } + window.$message?.success("新增分析对象成功") + editModal.value = false; } // 编辑分析对象 if(currentModalType.value==='edit' && currentModalObj.value==='obj'){ - const {error} = await hazopObjEditApi(contextTarget.value?.obj?.id,{obj_name: name.value}); - if(!error){ - getHazopNodes(); - window.$message?.success("编辑分析对象成功") - editModal.value = false; - } + nodes.value[contextTarget.value?.nodeIndex].objects[contextTarget.value?.objIndex].obj_name = name.value; + window.$message?.success("编辑分析对象成功") + editModal.value = false; } // 新增偏离 if(currentModalType.value==='add' && currentModalObj.value==='dev'){ - const {error} = await hazopDevAddApi(contextTarget.value?.obj?.id,{deviation: name.value}); - if(!error){ - getHazopNodes(); - window.$message?.success("新增偏离成功") - editModal.value = false; + let dev = { + id: 0, + object_id: nodes.value[contextTarget.value?.nodeIndex].objects[contextTarget.value?.objIndex].id, + deviation: name.value, + rows: [] } + if(nodes.value[contextTarget.value?.nodeIndex].objects[contextTarget.value?.objIndex].deviations){ + nodes.value[contextTarget.value?.nodeIndex].objects[contextTarget.value?.objIndex].deviations.push(dev); + }else { + nodes.value[contextTarget.value?.nodeIndex].objects[contextTarget.value?.objIndex].deviations = [dev]; + } + window.$message?.success("新增偏离成功") + editModal.value = false; } // 编辑偏离 if(currentModalType.value==='edit' && currentModalObj.value==='dev'){ - const {error} = await hazopDevEditApi(contextTarget.value?.dev?.id,{deviation: name.value}); - if(!error){ - getHazopNodes(); - window.$message?.success("编辑偏离成功") - editModal.value = false; - } + nodes.value[contextTarget.value?.nodeIndex].objects[contextTarget.value?.objIndex].deviations[contextTarget.value?.devIndex].deviation = name.value; + window.$message?.success("编辑偏离成功") + editModal.value = false; } - modalSaving.value = false; } // 打开删除弹框 const openDelModal = (type: string) => { - currentModalType.value = type; - delModal.value = true; -} - -// 删除确认 -const delConfirm = async () => { - delLoading.value = true; - // 删除节点 - if(currentModalType.value==='node'){ - const {error} = await hazopNodeDelApi(contextTarget.value?.node?.id); - if(!error){ - getHazopNodes(); - window.$message?.success("删除节点成功") - delModal.value = false; - } + let txt = '' + if(type==='node'){ + txt = `确定删除节点「${contextTarget.value?.node?.node_name}」吗?` } - // 删除分析对象 - if(currentModalType.value==='obj'){ - const {error} = await hazopObjDelApi(contextTarget.value?.obj?.id); - if(!error){ - getHazopNodes(); - window.$message?.success("删除分析对象成功") - delModal.value = false; - } + if(type==='obj'){ + txt = `确定删除分析对象「${contextTarget.value?.obj?.obj_name}」吗?` } - // 删除偏离 - if(currentModalType.value==='dev'){ - const {error} = await hazopDevDelApi(contextTarget.value?.dev?.id); - if(!error){ - getHazopNodes(); - window.$message?.success("删除偏离成功") - delModal.value = false; - } + if(type==='dev'){ + txt = `确定删除偏离「${contextTarget.value?.dev?.deviation}」吗?` } - delLoading.value = false; + dialog.info({ + title: '删除', + content: txt, + positiveText: '确定', + negativeText: '取消', + onPositiveClick: () => { + // 删除节点 + if(type==='node'){ + nodes.value.splice(contextTarget.value?.nodeIndex, 1); + window.$message?.success("删除节点成功") + } + // 删除分析对象 + if(type==='obj'){ + nodes.value[contextTarget.value?.nodeIndex].objects.splice(contextTarget.value?.objIndex, 1); + window.$message?.success("删除分析对象成功") + } + // 删除偏离 + if(type==='dev'){ + nodes.value[contextTarget.value?.nodeIndex].objects[contextTarget.value?.objIndex].deviations.splice(contextTarget.value?.devIndex, 1); + window.$message?.success("删除偏离成功") + } + }, + onNegativeClick: () => { + + } + }) } // 菜单选项配置(根据不同层级返回不同菜单) @@ -473,18 +443,15 @@ const getMenuOptions = (type: string) => { { label: '编辑节点', key: 'editNode' }, { label: '删除节点', key: 'delNode' }, ] - const objOptions = [ { label: '新增偏离', key: 'addDev' }, { label: '编辑分析对象', key: 'editObj' }, { label: '删除分析对象', key: 'delObj' }, ] - const devOptions = [ { label: '编辑分离', key: 'editDev' }, { label: '删除分离', key: 'delDev' }, ] - const map:any = { node: nodeOptions, obj: objOptions, @@ -503,11 +470,11 @@ const closeMenu = () => { } // 右键菜单事件处理 -const handleContextMenu = (event: MouseEvent, type: string, node: any, obj = null, dev = null) => { +const handleContextMenu = (event: MouseEvent, type: string, node: any, obj: any, dev: any, nodeIndex: number, objIndex: number, devIndex: number) => { // 阻止浏览器默认右键菜单 event.preventDefault() // 记录目标数据 - contextTarget.value = { type, node, obj, dev } + contextTarget.value = { type, node, obj, dev, nodeIndex, objIndex, devIndex } // 计算菜单位置 contextMenuX.value = Math.max(0, event.clientX) contextMenuY.value = Math.max(0, event.clientY) @@ -576,12 +543,14 @@ if (typeof window !== 'undefined') { // 获取HAZOP节点列表 const getHazopNodes = async () => { - treeLoading.value = true; + loading.value = true; const {data,error} = await hazopNodesApi(props.currentId); if(!error){ - nodes.value = data; + nodes.value = data.nodes; + selectedWorkers.value = data.users; + currentDev.value = nodes.value[0]?.objects[0]?.deviations?.[0] || null; } - treeLoading.value = false; + loading.value = false; } // 获取人员列表 @@ -589,9 +558,9 @@ const getWorkers = async () => { const {data,error} = await peopleListApi(); if(!error){ const transformed = data.map((item:any) => ({ - id: item.id, - name: item.realname, - org: item.user_departments.map((d:any) => d.label).join(';') + user_id: item.id, + user_name: item.realname, + org_name: item.user_departments.map((d:any) => d.label).join(';') })); workers.value = transformed; } @@ -619,13 +588,13 @@ onBeforeUnmount(() => { 返回工具列表 - 保存 + 保存 HAZOP 分析插件 插件 {{ `分析进度 ${devDone}/${devTotal} 个偏离 · ${recTotal} 条记录` }}
- 分析组成员 · 已选 {{ selectedWorkers.length }} 人 + 分析组成员 · 已选 {{ selectedWorkers.length }} 人
- + AI 整理入表 @@ -685,52 +654,50 @@ onBeforeUnmount(() => { >
- - - 新增节点 - -
-
-
- - -

节点{{ Number(nodeIndex) + 1 }}:{{ node.node_name }}

-
- + + 新增节点 + +
+
+
+ + +

节点{{ Number(nodeIndex) + 1 }}:{{ node.node_name }}

+
- +
@@ -741,7 +708,7 @@ onBeforeUnmount(() => { 单元格点击即可修改;风险等级按风险矩阵L / S 取值实时重算;「入表」将本条选入风险分析记录表
- +
@@ -765,13 +732,13 @@ onBeforeUnmount(() => {
操作
入表 + @click="enterToTable('single', Number(ri))">入表