发起变更草稿保存修改

This commit is contained in:
2026-09-10 14:33:46 +08:00
parent bb41d5a849
commit c36a5bbc85
6 changed files with 112 additions and 66 deletions
+20 -4
View File
@@ -5,6 +5,8 @@ import { useThemeStore } from '@/store/modules/theme';
import { useRoute } from 'vue-router';
import { getColorWithOpacity } from "@/utils/common";
import { localStg } from '@/utils/storage';
import { storeToRefs } from 'pinia';
import { useChangeStore } from '@/store/modules/change';
import { queryUnsavedApi, saveChangeApi, submitChangeApi, getChangeDetailApi } from '@/service/api/change';
import { systemConfigApi, templateListApi, } from '@/service/api/system';
import { orgListApi } from '@/service/api/user';
@@ -16,6 +18,8 @@ import Pssr from "./modules/pssr.vue";
import Evaluation from "./modules/evaluation.vue";
import CloseConfirm from "./modules/closeConfirm.vue";
const changeStore = useChangeStore();
const { getFlowInfo } = storeToRefs(changeStore);
const themeStore = useThemeStore();
const route = useRoute();
@@ -130,20 +134,32 @@ const confirmTab = (tab: string, val: boolean) => {
const getAllFormData = () => {
let changePreForm = changePreRef.value?.form;
let changeApplyForm = changeApplyRef.value?.form;
let questions = changeApplyRef.value?.questions;
let newQuestions = questions.map(({ question_id, score }:any) => ({ question_id, score }));
let changeRiskForm = riskAnalysisRef.value?.riskRecords;
let changeTrainingForm = changeTrainingRef.value?.form;
let pssrForm = pssrRef.value?.list;
let allItems = pssrForm.flatMap((group:any) => group.items ?? []);
let evaluationForm = evaluationRef.value?.list;
let flowList = getFlowInfo.value.map((item:any) => ({
members: item.selectedMember.map((m:any) => ({
user_id: m.user_id,
org_id: m.org_id,
org_name: m.org_name,
specialty: m.specialty === '' ? null : (m.specialty ?? null),
is_sign: m.is_sign
}))
}));
// let closeConfirmForm = closeConfirmRef.value?.form;
return {
title: title.value,
precheck:{...changePreForm},
apply_form:{...changeApplyForm},
approval_flow: {},
apply_form:{...changeApplyForm,level_score:newQuestions},
approval_flow: {steps:flowList},
risk_records:changeRiskForm,
train:{...changeTrainingForm},
pssr:{items:pssrForm},
accept_form:{...evaluationForm},
pssr:{items:allItems},
accept_form:{items:evaluationForm,purposes:changeApplyForm?.purposes,effect:changeApplyForm?.effect},
};
}
@@ -56,7 +56,6 @@ const form = ref<{
materials: string,
update_docs: string[],
disciplines: string[],
manage_dept: string,
plan_use_date: number | null,
risk_tools: string[],
}>({
@@ -73,7 +72,6 @@ const form = ref<{
materials: '',
update_docs: [],
disciplines: [],
manage_dept: '',
plan_use_date: null,
risk_tools: [],
});
@@ -92,9 +90,11 @@ const RISK_TOOLS_FORM = [
{ key: "SCL", label: "设备SCL" },
{ key: "RISK_CHECK", label: "风险检查表" },
];
const LEVEL_DIMS = ref([
const questions = ref<any>([
{
name: "1. 设备影响", note: "不合适的材质将会导致",
question_id: 1,
name: "1. 设备影响",
note: "不合适的材质将会导致",
score: 0,
options: [
{ label: "对设备带来一定的损害及腐蚀", score: 1 },
@@ -104,7 +104,9 @@ const LEVEL_DIMS = ref([
],
},
{
name: "2. 财务影响", note: "工艺设计错误将会导致",
question_id: 2,
name: "2. 财务影响",
note: "工艺设计错误将会导致",
score: 0,
options: [
{ label: "一定损失,a<1万人民币", score: 1 },
@@ -114,7 +116,9 @@ const LEVEL_DIMS = ref([
],
},
{
name: "3. 质量影响", note: "",
question_id: 3,
name: "3. 质量影响",
note: "",
score: 0,
options: [
{ label: "对质量带来一定的风险(非关键工席、非关键设备设施的变更)", score: 1 },
@@ -124,7 +128,9 @@ const LEVEL_DIMS = ref([
],
},
{
name: "4. 设计的复杂程度", note: "工艺设计包含了",
question_id: 4,
name: "4. 设计的复杂程度",
note: "工艺设计包含了",
score: 0,
options: [
{ label: "少量的简单系统(单个设备或单个部件)", score: 1 },
@@ -134,7 +140,9 @@ const LEVEL_DIMS = ref([
],
},
{
name: "5. 工艺设计的成熟度", note: "",
question_id: 5,
name: "5. 工艺设计的成熟度",
note: "",
score: 0,
options: [
{ label: "被频繁使用,且被证明为专利方设计", score: 1 },
@@ -144,7 +152,9 @@ const LEVEL_DIMS = ref([
],
},
{
name: "6. 工艺危害", note: "物料性质和工艺条件",
question_id: 6,
name: "6. 工艺危害",
note: "物料性质和工艺条件",
score: 0,
options: [
{ label: "无危害(常压、-10℃≤温度<40℃、无毒、无腐蚀不燃)", score: 1 },
@@ -250,14 +260,14 @@ const useAiLevel = async () => {
if (levelAiLoading.value) return;
levelAiLoading.value = true;
try {
const dims = LEVEL_DIMS.value.map((d:any, i:any) => ({ dim: `DIM_${i + 1}`, name: d.name, max: Math.max(...d.options.map((o:any) => o.score)) }));
const dims = questions.value.map((d:any, i:any) => ({ dim: `DIM_${i + 1}`, name: d.name, max: Math.max(...d.options.map((o:any) => o.score)) }));
const res = await aiLevelScore(form.description, (form.compare_rows ?? []).map((x: any) => ({ item: x.item, before_text: x.before_text, after_text: x.after_text })), dims);
// 重新评分前清零,避免多次点击分数累加
totalScore.value = 0;
levelAiBasis.value = [];
res?.dims?.forEach((d, i) => {
totalScore.value += d.score ?? 0;
LEVEL_DIMS.value[i].score = d.score ?? 0;
questions.value[i].score = d.score ?? 0;
levelAiBasis.value[i] = d.basis ?? "";
});
levelAiText.value = res?.analysis_text ?? "";
@@ -316,9 +326,9 @@ const onRegenerate = async () => {
}
// 处理等级判定表评分变化
const handleScoreChange = (val:number,index:number) => {
LEVEL_DIMS.value[index].score = val;
questions.value[index].score = val;
let score = 0;
LEVEL_DIMS.value.forEach((d:any) => {
questions.value.forEach((d:any) => {
score += d.score;
});
totalScore.value = score;
@@ -398,7 +408,7 @@ const confirmTab = (tab:string, val:boolean) => {
emit('confirmTab', tab, val);
}
defineExpose({form, applyAiResult, generateLevel: useAiLevel})
defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
</script>
@@ -727,16 +737,16 @@ defineExpose({form, applyAiResult, generateLevel: useAiLevel})
</div>
</div>
<div class="grid gap-3 grid-cols-2">
<div v-for="(dim, di) in LEVEL_DIMS" :key="dim.name" class="rounded-lg border p-3">
<div v-for="(dim, di) in questions" :key="dim.name" class="rounded-lg border p-3">
<div class="mb-2 flex items-baseline justify-between gap-2">
<span class="text-sm font-semibold text-slate-700">
{{ dim.name }}
<span v-if="dim.note" class="ml-1 text-xs font-normal text-slate-400">{{ dim.note }}</span>
</span>
<span class="text-sm text-slate-500">得分 <b class="text-[#17407F]">{{ LEVEL_DIMS[di].score }}</b></span>
<span class="text-sm text-slate-500">得分 <b class="text-[#17407F]">{{ questions[di].score }}</b></span>
</div>
<div class="space-y-1.5">
<NRadioGroup v-model:value="LEVEL_DIMS[di].score" size="small" :disabled="levelAiLoading" name="radiogroup" :on-update:value="(v)=>handleScoreChange(v,di)">
<NRadioGroup v-model:value="questions[di].score" size="small" :disabled="levelAiLoading" name="radiogroup" :on-update:value="(v)=>handleScoreChange(v,di)">
<div class="flex flex-col justify-center gap-2">
<NRadio v-for="op in dim.options" :key="op.score" :value="op.score">
<p class="text-xs">{{ op.label }}{{ op.score }}</p>
@@ -756,7 +766,7 @@ defineExpose({form, applyAiResult, generateLevel: useAiLevel})
</div>
<div class="mt-2 space-y-3 text-xs leading-relaxed text-slate-600">
<p v-if="levelAiText">{{ levelAiText }}</p>
<p v-for="(dim, di) in LEVEL_DIMS" :key="dim.name">
<p v-for="(dim, di) in questions" :key="dim.name">
<template v-if="levelAiBasis[di]"><b class="text-[#17407F]">{{ dim.name }}</b>{{ levelAiBasis[di] }}</template>
</p>
<p class="text-slate-400">各维度得分均可人工改判修改留痕改判后等级自动回填至申请表</p>
+16 -8
View File
@@ -174,10 +174,14 @@ const findRowWithParents = (rowIndex: number = -1) => {
...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
item: `${currentDev.value?.deviation}${currentNode.value?.node_name?`${currentNode.value?.node_name}${currentObj.value?.obj_name?`-${currentObj.value?.obj_name}`:''}`:''}`,
inherent_level: row?.risk_value || '',
safeguards: row?.safeguards?.map((item:any) => item.name).join('') || '',
suggestions: row?.suggestions?.map((item:any) => item.name).join('') || '',
residual_level: countRiskLevel(row)?.name || '',
to_pssr: 0,
safes: row?.safeguards || [],
sugs: row?.suggestions || [],
};
}
return null;
@@ -203,10 +207,14 @@ const enterToTable = (type: string, rowIndex: number = -1) => {
...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
item: `${deviation}${node_ame?`${node_ame}${obj_ame?`-${obj_ame}`:''}`:''}`,
inherent_level: row?.risk_value || '',
safeguards: row?.safeguards?.map((item:any) => item.name).join('') || '',
suggestions: row?.suggestions?.map((item:any) => item.name).join('') || '',
residual_level: countRiskLevel(row)?.name || '',
to_pssr: 0,
safes: row?.safeguards || [],
sugs: row?.suggestions || [],
});
});
}
+19 -11
View File
@@ -131,25 +131,33 @@ const enterToTable = (type: string, row: any, stepIdx: number) => {
...row,
scene: `${row.cause?row.cause+';':''}${row.consequence?'可能导致:'+row.consequence:''}`,
source: 'JSA',
risk_item: `${step.step_name?step.step_name:''}`,
safe: row?.safeguards?.map((item:any) => item.name).join('') || '',
suggestion: row?.suggestions?.map((item:any) => item.name).join('') || '',
to_pssr: 0
item: `${step.step_name?step.step_name:''}`,
inherent_level: row?.risk_value || '',
safeguards: row?.safeguards?.map((item:any) => item.name).join('') || '',
suggestions: row?.suggestions?.map((item:any) => item.name).join('') || '',
residual_level: countRiskLevel(row)?.name || '',
to_pssr: 0,
safes: row?.safeguards || [],
sugs: row?.suggestions || [],
});
emit('toTable',type,enrichedRow);
}
if(type==='all'){
const enrichedRows:any[] = [];
steps.value.forEach((step:any) => {
step?.risks?.forEach((risk:any) => {
step?.risks?.forEach((row:any) => {
enrichedRows.push({
...risk,
scene: `${risk.cause?risk.cause+';':''}${risk.consequence?'可能导致:'+risk.consequence:''}`,
...row,
scene: `${row.cause?row.cause+';':''}${row.consequence?'可能导致:'+row.consequence:''}`,
source: 'JSA',
risk_item: `${step.step_name}`,
safe: risk?.safeguards?.map((item:any) => item.name).join('') || '',
suggestion: risk?.suggestions?.map((item:any) => item.name).join('') || '',
to_pssr: 0
item: `${step.step_name}`,
inherent_level: row?.risk_value || '',
safeguards: row?.safeguards?.map((item:any) => item.name).join('') || '',
suggestions: row?.suggestions?.map((item:any) => item.name).join('') || '',
residual_level: countRiskLevel(row)?.name || '',
to_pssr: 0,
safes: row?.safeguards || [],
sugs: row?.suggestions || [],
});
})
})
@@ -63,7 +63,7 @@ const jsaRiskLevel = (l: number, s: number) => {
const countHazopRiskLevel = (row: any) => {
const l = row.l_value;
const s = row.s_value;
const newArr = [...row?.safeguards || [],...row?.suggestions || []];
const newArr = [...row?.safes || [],...row?.sugs || []];
const result:any = { L: 0, S: 0 };
newArr.forEach(item => {
if (!item.value) return; // 跳过空值
@@ -83,7 +83,7 @@ const countHazopRiskLevel = (row: any) => {
const countJsaRiskLevel = (row: any) => {
const l = row.l_value;
const s = row.s_value;
const newArr = [...row?.safeguards || [],...row?.suggestions || []];
const newArr = [...row?.safes || [],...row?.sugs || []];
const result:any = { L: 0, S: 0 };
newArr.forEach(item => {
if (!item.value) return; // 跳过空值
@@ -287,7 +287,7 @@ onMounted(() => {
<tbody>
<tr v-for="(rec, i) in riskRecords" :key="i">
<td class="border px-1.5 py-1 font-medium text-slate-700">
<NInput type="textarea" size="small" v-model:value="rec.risk_item" :autosize="{minRows:1}" class="text-xs" />
<NInput type="textarea" size="small" v-model:value="rec.item" :autosize="{minRows:1}" class="text-xs" />
</td>
<td class="border px-1.5 py-1">
<NInput type="textarea" size="small" v-model:value="rec.scene" :autosize="{minRows:1}" class="text-xs" />
@@ -295,28 +295,28 @@ onMounted(() => {
<td class="border px-1.5 py-1 text-center align-top">
<div v-if="rec.source==='RISK'">
<NTag
v-if="rec.risk_value && rec.risk_value === '低'"
v-if="rec.inherent_level && rec.inherent_level === '低'"
class="text-[11px]"
size="small"
type="success"
>
{{rec.risk_value}}
{{rec.inherent_level}}
</NTag>
<NTag
v-if="rec.risk_value && rec.risk_value === '中'"
v-if="rec.inherent_level && rec.inherent_level === '中'"
class="text-[11px]"
size="small"
type="warning"
>
{{rec.risk_value}}
{{rec.inherent_level}}
</NTag>
<NTag
v-if="rec.risk_value && rec.risk_value === '高'"
v-if="rec.inherent_level && rec.inherent_level === '高'"
class="text-[11px]"
size="small"
type="error"
>
{{rec.risk_value}}
{{rec.inherent_level}}
</NTag>
</div>
<div v-if="rec.source==='HAZOP'">
@@ -349,36 +349,36 @@ onMounted(() => {
</div>
</td>
<td class="border px-1.5 py-1">
<NInput type="textarea" size="small" v-model:value="rec.safe" :autosize="{minRows:1}" class="text-xs" />
<NInput type="textarea" size="small" v-model:value="rec.safeguards" :autosize="{minRows:1}" class="text-xs" />
</td>
<td class="border px-1.5 py-1">
<NInput type="textarea" size="small" v-model:value="rec.suggestion" :autosize="{minRows:1}" class="text-xs" />
<NInput type="textarea" size="small" v-model:value="rec.suggestions" :autosize="{minRows:1}" class="text-xs" />
</td>
<td class="border px-1.5 py-1 text-center align-top">
<div v-if="rec.source==='RISK'">
<NTag
v-if="rec.residual_risk_value && rec.residual_risk_value === '低'"
v-if="rec.residual_level && rec.residual_level === '低'"
class="text-[11px]"
size="small"
type="success"
>
{{rec.residual_risk_value}}
{{rec.residual_level}}
</NTag>
<NTag
v-if="rec.residual_risk_value && rec.residual_risk_value === '中'"
v-if="rec.residual_level && rec.residual_level === '中'"
class="text-[11px]"
size="small"
type="warning"
>
{{rec.residual_risk_value}}
{{rec.residual_level}}
</NTag>
<NTag
v-if="rec.residual_risk_value && rec.residual_risk_value === '高'"
v-if="rec.residual_level && rec.residual_level === '高'"
class="text-[11px]"
size="small"
type="error"
>
{{rec.residual_risk_value}}
{{rec.residual_level}}
</NTag>
</div>
<div v-if="rec.source==='HAZOP'">
@@ -456,7 +456,7 @@ onMounted(() => {
<NDrawer v-model:show="pluginDrawer" width="85%" placement="right">
<NDrawerContent>
<template v-if="currentPlugin === 'RISK'">
<RiskPre :isAiOpen="props.isAiOpen" :currentId="props.currentId" :jsaRiskMatrixConfig="jsaRiskMatrixConfig" @close="closePlugin" @toTable="exportAll" />
<RiskPre :isAiOpen="props.isAiOpen" :currentId="props.currentId" @close="closePlugin" @toTable="exportAll" />
</template>
<template v-if="currentPlugin === 'HAZOP'">
<Hazop :isAiOpen="props.isAiOpen" :currentId="props.currentId" :hazopRiskMatrixConfig="hazopRiskMatrixConfig" @close="closePlugin" @toTable="exportAll" />
@@ -6,7 +6,7 @@ import { peopleListApi } from '@/service/api/system';
import { riskPreDetailApi, riskPreSaveApi } from '@/service/api/plugin';
const themeStore = useThemeStore();
const props = defineProps(['isAiOpen','currentId','jsaRiskMatrixConfig'])
const props = defineProps(['isAiOpen','currentId'])
const emit = defineEmits(['close','toTable']);
// 表格样式
const TH = "border bg-slate-100 px-2 py-1.5 text-[11px] font-medium text-slate-500";
@@ -77,9 +77,11 @@ const enterToTable = (type: string, row: any) => {
...row,
scene: row?.scene_desc || '',
source: 'RISK',
risk_item: row?.risk_item || '',
safe: row?.safeguards || '',
suggestion: row?.suggestions || '',
item: row?.risk_item || '',
inherent_level: row?.risk_value || '',
safeguards: row?.safeguards || '',
suggestions: row?.suggestions || '',
residual_level: row?.residual_risk_value || '',
to_pssr: 0
});
emit('toTable',type,enrichedRow);
@@ -91,9 +93,11 @@ const enterToTable = (type: string, row: any) => {
...row,
scene: row?.scene_desc || '',
source: 'RISK',
risk_item: row?.risk_item || '',
safe: row?.safeguards || '',
suggestion: row?.suggestions || '',
item: row?.risk_item || '',
inherent_level: row?.risk_value || '',
safeguards: row?.safeguards || '',
suggestions: row?.suggestions || '',
residual_level: row?.residual_risk_value || '',
to_pssr: 0
});
})