发起变更提交参数修改

This commit is contained in:
2026-09-11 08:58:50 +08:00
parent 8e2bc84b02
commit 7030414146
8 changed files with 177 additions and 53 deletions
+63 -39
View File
@@ -35,6 +35,13 @@ const changeTrainingRef = ref<any>(null);
const pssrRef = ref<any>(null);
const evaluationRef = ref<any>(null);
const closeConfirmRef = ref<any>(null);
// 表单数据
const changePreForm = ref<any>(null)
const changeApplyForm = ref<any>(null)
const riskAnalysisForm = ref<any>(null)
const changeTrainingForm = ref<any>(null)
const pssrForm = ref<any>(null)
const evaluationForm = ref<any>(null)
// 组织树
const orgTree = ref<any>([]);
// 变更类型选项
@@ -54,7 +61,6 @@ const changeNo = ref<string>('');
const currentId = ref<number>(0);
// 是否确认
const confirmed = ref<{[key: string]: boolean}>({
'pre': false,
'form': false,
'risk': false,
'train': false,
@@ -63,9 +69,6 @@ const confirmed = ref<{[key: string]: boolean}>({
// 'close': false,
});
// 变更预分析表单数据
const changePreForm = ref<any>(null)
const changeApplyForm = ref<any>(null)
// 当前申请表所选变更类型的文本编码(供 PSSR 等 AI 接口使用)
const applyTypeLabel = computed(() => typeList.value.find((t) => t.value === changeApplyForm.value?.change_type)?.label ?? '');
@@ -95,12 +98,12 @@ const onApplyGenerated = async (res: any) => {
// **********************tab切换***********************
const TABS = [
{ id: "pre", name: "变更预识别", icon: 'lucide:sparkles' },
{ id: "form", name: "变更申请表", icon: 'akar-icons:file' },
{ id: "risk", name: "风险分析记录表", icon: 'proicons:alert-triangle' },
{ id: "train", name: "变更培训内容", icon: 'lucide:graduation-cap' },
{ id: "pssr", name: "PSSR 检查内容", icon: 'tabler:clipboard-check' },
{ id: "evaluation", name: "验收评价", icon: 'material-symbols:check-circle-outline' },
{ step: 0, id: "pre", name: "变更预识别", icon: 'lucide:sparkles' },
{ step: 1, id: "form", name: "变更申请表", icon: 'akar-icons:file' },
{ step: 2, id: "risk", name: "风险分析记录表", icon: 'proicons:alert-triangle' },
{ step: 3, id: "train", name: "变更培训内容", icon: 'lucide:graduation-cap' },
{ step: 4, id: "pssr", name: "PSSR 检查内容", icon: 'tabler:clipboard-check' },
{ step: 5,id: "evaluation", name: "验收评价", icon: 'material-symbols:check-circle-outline' },
// { id: "close", name: "变更关闭确认表", icon: 'quill:folder-open' },
];
// 当前标签
@@ -215,36 +218,28 @@ const changeApplyRules = (form:any) => {
// 提交
const submitConfirm = async (type: string) => {
// if(aiEnabled.value){
// const allTrue = Object.values(confirmed.value).every(value => value === true);
// if(!allTrue){
// return window.$message?.warning('全部标签页确认后方可提交(标签栏已用红点标出)');
// }
// }
let form = getAllFormData();
// if(form.title.trim() === ''){
// return window.$message?.warning('请填写变更名称');
// }
// if(aiEnabled.value){
// if(!changePreForm.value?.description){
// return window.$message?.warning('请填写变更内容描述,在「变更预识别」标签页填写');
// }
// }
if(form.title.trim() === ''){
return window.$message?.warning('请填写变更名称');
}
if(aiEnabled.value){
if(!changePreForm.value?.description){
return window.$message?.warning('请填写变更内容描述,在「变更预识别」标签页填写');
}
}
// 变更申请表
// if(type==='form'){
// if(changeApplyRules(form)){
// return;
// }
// }
// 变更培训内容
// if(type==='train'){
// if(form.train.content.trim() === ''){
// return window.$message?.warning('请填写培训内容,在「变更培训内容」标签页填写');
// }
// }
if(type==='form'){
if(changeApplyRules(form)){
return;
}
}
if(aiEnabled.value){
const allTrue = Object.values(confirmed.value).every(value => value === true);
if(!allTrue){
return window.$message?.warning('全部标签页确认后方可提交(标签栏已用红点标出)');
}
}
const {error} = await submitChangeApi(currentId.value, form);
if(!error){
window.$message?.success("提交成功");
@@ -255,7 +250,25 @@ const submitConfirm = async (type: string) => {
const getChangeDetail = async () => {
const {data,error} = await getChangeDetailApi(currentId.value);
if(!error){
title.value = data.title;
changeNo.value = data.change_no;
changePreForm.value = data.precheck;
changeApplyForm.value = data.apply_form;
riskAnalysisForm.value = data.risk_records;
changeTrainingForm.value = data.train;
pssrForm.value = data.pssr;
evaluationForm.value = data.accept_form;
// 解析每个步骤是否完成
// 构建 step_no -> status 的映射
const statusMap = new Map(data?.steps?.map((step:any) => [step.step_no, step.status]));
// 生成结果
const result:any = {};
TABS.filter(({ id }) => id !== 'pre').forEach(({ step, id }) => {
const status = statusMap.get(step);
result[id] = status === 1; // status 为 1 时 true,其它(含无匹配)为 false
});
confirmed.value = result;
}
}
// 查询变更未保存数据
@@ -357,7 +370,8 @@ onMounted(() => {
<ChangePre
ref="changePreRef"
:title="title"
:currentId="currentId"
:currentId="currentId"
:currentForm="changePreForm"
@save="saveDraft"
@apply-generated="onApplyGenerated"
/>
@@ -375,6 +389,8 @@ onMounted(() => {
:title="title"
:currentId="currentId"
:formInfo="changePreForm"
:currentForm="changeApplyForm"
:confirmed="confirmed[tab]"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@@ -391,6 +407,8 @@ onMounted(() => {
:currentId="currentId"
:formInfo="changeApplyForm"
:preInfo="changePreForm"
:currentForm="riskAnalysisForm"
:confirmed="confirmed[tab]"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@@ -405,6 +423,8 @@ onMounted(() => {
:title="title"
:currentId="currentId"
:formInfo="changePreForm"
:currentForm="changeTrainingForm"
:confirmed="confirmed[tab]"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@@ -420,6 +440,8 @@ onMounted(() => {
:currentId="currentId"
:formInfo="changePreForm"
:changeType="applyTypeLabel"
:currentForm="pssrForm"
:confirmed="confirmed[tab]"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@@ -435,6 +457,8 @@ onMounted(() => {
:title="title"
:currentId="currentId"
:formInfo="changeApplyForm"
:currentForm="evaluationForm"
:confirmed="confirmed[tab]"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@@ -1,6 +1,6 @@
<!-- 变更申请表 -->
<script setup lang="ts">
import { ref, computed, h } from 'vue';
import { ref, computed, h, watch } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { tagTextColor, tagBgColor, tagBorderColor } from '@/utils/common';
@@ -17,9 +17,23 @@ import 'vue3-office-preview/lib/style.css'
const themeStore = useThemeStore();
const changeStore = useChangeStore();
const props = defineProps(['type','orgTree','templateList','typeList','levelList','isAiOpen','title','currentId','formInfo'])
const props = defineProps(['type','orgTree','templateList','typeList','levelList','isAiOpen','title','currentId','formInfo', 'currentForm', 'confirmed'])
const emit = defineEmits(['save','submit','confirmTab','update:title']);
watch(() => props.currentForm, (newVal:any) => {
// 过滤出需要的字段
const {
approval_flow,
attachments,
change_id,
created_at,
id,
updated_at,
...rest
} = newVal;
form.value = rest;
})
const aiFail = (e: any) => {
if (e instanceof AiError) window.$message?.warning(`AI 分析失败:${e.message},可手动填写`);
else window.$message?.warning("AI 分析失败,可手动填写");
@@ -671,6 +685,7 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
:title="props.title"
:isAiOpen="props.isAiOpen"
:currentForm="form"
:confirmed="props.confirmed"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@@ -1,6 +1,6 @@
<!-- 变更预识别 -->
<script setup lang="ts">
import { inject, ref } from 'vue';
import { inject, watch, ref } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { AiError, aiApplication, aiRecognize, aiRiskPreAnalysis } from "@/service/api/ai";
@@ -8,6 +8,7 @@ import ChangeCompareTable from "./ChangeCompareTable.vue";
const themeStore = useThemeStore();
const props = defineProps(['title','currentId','currentForm'])
const emit = defineEmits(['save', 'apply-generated'])
const generalLoading = inject('generalLoading', {saveLoading: ref<boolean>(false),btnDisabled: ref<boolean>(false)})
const {saveLoading,btnDisabled} = generalLoading
@@ -113,6 +114,20 @@ const saveDraft = () => {
defineExpose({form})
watch(() => props.currentForm, (newVal:any) => {
if(newVal.severity){
preRiskShow.value = true;
}
const {
attachments,
change_id,
created_at,
id,
updated_at,
...rest
} = newVal;
form.value = rest;
})
</script>
<template>
@@ -174,7 +189,7 @@ defineExpose({form})
{{ preRiskEdit === s.key ? "完成" : "编辑" }}
</NButton>
</div>
<NInput v-model:value="form[s.key]" :readonly="preRiskEdit !== s.key" type="textarea" :bordered="false" autofocus />
<NInput v-model:value="form[s.key]" :readonly="preRiskEdit !== s.key" type="textarea" :autosize="{ minRows: 2 }" :bordered="false" autofocus />
</div>
<div class="rounded-lg bg-amber-50 px-3 py-2 text-xs leading-5 text-amber-700">
AI 风险预分析仅供参考用于申请阶段的快速研判正式风险识别请前往风险分析记录表标签页使用 HAZOP / JSA / 检查表法开展并留存记录
@@ -1,6 +1,6 @@
<!-- 变更培训内容 -->
<script setup lang="ts">
import { ref } from 'vue';
import { ref, watch } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { AiError, aiTraining } from "@/service/api/ai";
@@ -8,9 +8,22 @@ import Footer from './footer.vue';
const themeStore = useThemeStore();
const props = defineProps(['type','isAiOpen','title','currentId','formInfo'])
const props = defineProps(['type','isAiOpen','title','currentId','formInfo', 'currentForm', 'confirmed'])
const emit = defineEmits(['save','submit','confirmTab']);
watch(() => props.currentForm, (newVal:any) => {
// 过滤出需要的字段
const {
attachments,
change_id,
created_at,
id,
updated_at,
...rest
} = newVal;
form.value = rest;
})
const footerRef = ref<any>(null);
const aiFail = (e: any) => {
@@ -92,6 +105,7 @@ defineExpose({form, onRegenerate})
:currentId="props.currentId"
:title="props.title"
:isAiOpen="props.isAiOpen"
:confirmed="props.confirmed"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@@ -1,6 +1,6 @@
<!-- 验收评价 -->
<script setup lang="ts">
import { h, ref } from 'vue';
import { h, ref, watch } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { getColorWithOpacity } from "@/utils/common";
@@ -8,9 +8,17 @@ import { AiError, aiAcceptance } from "@/service/api/ai";
import Footer from './footer.vue';
const themeStore = useThemeStore();
const props = defineProps(['type','orgTree','isAiOpen','title','currentId','formInfo'])
const props = defineProps(['type','orgTree','isAiOpen','title','currentId','formInfo','currentForm','confirmed'])
const emit = defineEmits(['save','submit','confirmTab']);
watch(() => props.currentForm, (newVal:any) => {
const omit = (obj:any, fields: string[]) => Object.fromEntries(Object.entries(obj).filter(([k]) => !fields.includes(k)));
const result = newVal?.items.map((item:any) => omit(item, [
'created_at', 'updated_at', 'change_id', 'org_name'
]));
list.value = result || [];
})
// 是否是ai生成
const isAiGenerated = ref<boolean>(false);
const footerRef = ref<any>(null);
@@ -284,6 +292,7 @@ defineExpose({list, onRegenerate})
:currentId="props.currentId"
:title="props.title"
:isAiOpen="props.isAiOpen"
:confirmed="props.confirmed"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@@ -1,6 +1,6 @@
<!-- 通用底部操作 -->
<script setup lang="ts">
import { computed, inject, ref } from 'vue';
import { computed, inject, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { Icon } from '@iconify/vue'
import { useChangeStore } from '@/store/modules/change';
@@ -14,10 +14,15 @@ const props = defineProps([
'currentId',
'title',
'isAiOpen',
'currentForm'
'currentForm',
'confirmed'
])
const emit = defineEmits(['save','submit','confirmTab','regenerate']);
watch(() => props.confirmed, (newVal:boolean) => {
confirmed.value = newVal;
})
const generalLoading = inject('generalLoading', {saveLoading: ref<boolean>(false),btnDisabled: ref<boolean>(false)})
const {saveLoading,btnDisabled} = generalLoading
+35 -2
View File
@@ -1,6 +1,6 @@
<!-- PSSR检查内容 -->
<script setup lang="ts">
import { ref } from 'vue';
import { ref, watch } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import Footer from './footer.vue';
@@ -8,9 +8,41 @@ import { AiError, aiPssr } from "@/service/api/ai";
import { uploadFileApi } from "@/service/api/file";
const themeStore = useThemeStore();
const props = defineProps(['type','isAiOpen','title','currentId','formInfo','changeType'])
const props = defineProps(['type','isAiOpen','title','currentId','formInfo','changeType','currentForm','confirmed'])
const emit = defineEmits(['save','submit','confirmTab']);
watch(() => props.currentForm, (newVal:any) => {
// 1. 按 group_type 分组
const map = new Map();
newVal?.items?.forEach((item:any) => {
const t = item.group_type;
if (!map.has(t)) map.set(t, []);
map.get(t).push(item);
});
// 2. 生成目标数组
const result = list.value.map(({ type, g }:any) => {
const items = map.get(type) || [];
// na_flag 取该组第一个元素的 na_flag,无数据时默认为 0
const na_flag = items.length > 0 ? items[0].na_flag : 0;
return {
g,
type,
na_flag,
items
};
});
// 3. 过滤掉不需要的字段
const omitFields = ['created_at', 'updated_at', 'change_id', 'group_type', 'group_name'];
const newResult = result.map((group:any) => ({
...group,
items: group.items.map((item:any) =>
Object.fromEntries(Object.entries(item).filter(([k]) => !omitFields.includes(k)))
)
}));
list.value = newResult;
})
const footerRef = ref<any>(null);
const uploadLoading = ref<boolean>(false);
const uploadFileRef = ref<any>(null);
@@ -211,6 +243,7 @@ defineExpose({list, onRegenerate})
:currentId="props.currentId"
:title="props.title"
:isAiOpen="props.isAiOpen"
:confirmed="props.confirmed"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@@ -1,6 +1,6 @@
<!-- 风险分析记录表 -->
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { computed, onMounted, ref, watch } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import RiskPre from "./riskPre.vue";
@@ -13,9 +13,17 @@ import { matrixConfigApi } from "@/service/api/system";
const themeStore = useThemeStore();
const props = defineProps(['type','isAiOpen','title','currentId','formInfo'])
const props = defineProps(['type','isAiOpen','title','currentId','formInfo', 'currentForm', 'confirmed'])
const emit = defineEmits(['save','submit','confirmTab']);
watch(() => props.currentForm, (newVal:any) => {
const omit = (obj:any, fields: string[]) => Object.fromEntries(Object.entries(obj).filter(([k]) => !fields.includes(k)));
const result = newVal?.records.map((item:any) => omit(item, [
'id', 'created_at', 'updated_at', 'change_id', 'source_row_id'
]));
riskRecords.value = result || [];
})
const hazopRiskMatrixConfig = ref<any>(null);
const jsaRiskMatrixConfig = ref<any>(null);
const pluginDone = computed(() =>
@@ -448,6 +456,7 @@ onMounted(() => {
:title="props.title"
:isAiOpen="props.isAiOpen"
:currentForm="formInfo"
:confirmed="props.confirmed"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"