发起变更审批流程弹框板块修改

This commit is contained in:
2026-10-09 15:12:01 +08:00
parent 5b981d4fa6
commit a5282019c0
8 changed files with 296 additions and 152 deletions
@@ -1,42 +1,24 @@
<!-- 变更申请表 -->
<script setup lang="ts">
import { ref, computed, h, watch } from 'vue';
import { ref, computed, h, watch, nextTick } from 'vue';
import { Icon } from '@iconify/vue'
import { useThemeStore } from '@/store/modules/theme';
import { tagTextColor, tagBgColor, tagBorderColor, downloadFileFun } from '@/utils/common';
import { localStg } from '@/utils/storage';
import { useChangeStore } from '@/store/modules/change';
import { AiError, aiTypeJudge, aiLevelScore, aiApplication } from "@/service/api/ai";
import { uploadFileApi, deleteFileApi } from "@/service/api/file";
import Footer from './footer.vue'
import previewComponent from '@/components/previewFile/index.vue'
const themeStore = useThemeStore();
const changeStore = useChangeStore();
const props = defineProps(['type','orgTree','templateList','typeList','levelList','isAiOpen','title','currentId','formInfo', 'currentForm', 'confirmed','members'])
const emit = defineEmits(['save','submit','confirmTab','update:title']);
const props = defineProps([
'type','orgTree','templateList','typeList','levelList','isAiOpen',
'title','currentId','formInfo', 'currentForm', 'confirmed','members','steps'
])
const emit = defineEmits(['save','submit','confirmTab','update:title','update:steps']);
watch(() => props.currentForm, (newVal:any) => {
// 过滤出需要的字段
const {
approval_flow,
attachments,
change_id,
created_at,
id,
updated_at,
...rest
} = newVal;
form.value = rest;
// 用 Map 建立 question_id -> score 的映射,查找 O(1)
const scoreMap = new Map(form.value?.level_score?.map((item:any) => [item.question_id, item.score]) || []);
questions.value = questions.value.map((item:any) => ({
...item,
score: scoreMap.has(item.question_id) ? scoreMap.get(item.question_id) : item.score
}));
totalScore.value = questions.value.reduce((sum:number, item:any) => sum + (item.score || 0), 0);
})
const isSyncingFromParent = ref(false);
const aiFail = (e: any) => {
if (e instanceof AiError) window.$message?.warning(`AI 分析失败:${e.message},可手动填写`);
@@ -58,11 +40,10 @@ const previewFile = ref<any>(null);
const sheet = ref<string>('');
// 上传文件
const uploadFile = ref<{id:number, file_name:string, file_url:string, uploader_name:string, file_type:string, created_at:string, version:string | null}[]>([]);
// 表单
const form = ref<{
change_type: number | null,
change_level: any,
change_level: number | null,
duration_type: number | null,
restore_deadline: number | null,
org_id: number | null,
@@ -79,7 +60,7 @@ const form = ref<{
level_score: any[],
}>({
change_type: null,
change_level: computed(() => totalScore.value >= 20 ? 2 : 1),
change_level: null,
duration_type: null,
restore_deadline: null,
org_id: null,
@@ -185,10 +166,59 @@ const questions = ref<any>([
},
]);
// 当前模板步骤
const currentSteps = ref<any>(null);
watch(() => props.currentForm, (newVal:any) => {
if (!newVal) return;
isSyncingFromParent.value = true;
// 过滤出需要的字段
const {
approval_flow,
attachments,
change_id,
created_at,
id,
updated_at,
...rest
} = newVal;
form.value = rest;
// 用 Map 建立 question_id -> score 的映射,查找 O(1)
const scoreMap = new Map(form.value?.level_score?.map((item:any) => [item.question_id, item.score]) || []);
questions.value = questions.value.map((item:any) => ({
...item,
score: scoreMap.has(item.question_id) ? scoreMap.get(item.question_id) : item.score
}));
totalScore.value = questions.value.reduce((sum:number, item:any) => sum + (item.score || 0), 0);
nextTick(() => { // ★ form 的 watch 跑完后再释放
isSyncingFromParent.value = false;
});
},{ immediate: true })
// 监听总分数变化,自动更新等级
watch(() => totalScore.value, (v:number) => {
form.value.change_level = v >= 20 ? 2 : 1;
});
// 变更类型 / 变更等级 / 变更时限,任一改变 → 更新模板
watch(
() => [
form.value?.change_type,
form.value?.change_level,
form.value?.duration_type,
],
() => {
if (isSyncingFromParent.value) return;
updateCurrentTemplate()
}
);
// 监听步骤变化,更新本地步骤
watch(() => props.steps, (v:any) => {
currentSteps.value = v;
},{ immediate: true })
// 筛选涉及专业的人员和部门
const filterMembersBySpecialties = (data: any, specialties: string[]) => {
const filterMembersBySpecialties = (steps: any, specialties: string[]) => {
const result: any[] = [];
data.steps.forEach((step: any) => {
steps.forEach((step: any) => {
step.members.forEach((member: any) => {
if (member.specialty && specialties.includes(member.specialty)) {
result.push({
@@ -200,27 +230,20 @@ const filterMembersBySpecialties = (data: any, specialties: string[]) => {
});
return result;
}
// 当前模板
const currentTemplate = computed(() => {
let template = null;
if(props.currentForm?.approval_flow?.steps && props.currentForm?.approval_flow?.steps.length){
template = props.currentForm?.approval_flow;
}else{
template = props.templateList.find((item: any) =>
item.level === form.value?.change_level &&
item.category === form.value?.change_type &&
item.duration === form.value?.duration_type
) || null;
}
footerRef.value?.clearApprovers();
changeStore.setStepsInfo(template?template.steps:[]);
return template?template:null;
});
// 更新当前模板步骤
const updateCurrentTemplate = () => {
let template = props.templateList.find((item: any) =>
item.level === form.value?.change_level &&
item.category === form.value?.change_type &&
item.duration === form.value?.duration_type
) || null;
currentSteps.value = template?.steps || [];
emit('update:steps', currentSteps.value);
}
// 联动专业会签人员
const signers = computed(() => {
if(currentTemplate.value){
const matchedMembers = filterMembersBySpecialties(currentTemplate.value, form.value.disciplines);
if(currentSteps.value){
const matchedMembers = filterMembersBySpecialties(currentSteps.value || [], form.value.disciplines);
return matchedMembers
}
return null
@@ -414,6 +437,11 @@ const confirmTab = (tab:string, val:boolean) => {
emit('confirmTab', tab, val);
}
// 更新审批流程
const updateSteps = (steps:any[]) => {
emit('update:steps', steps);
}
defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
</script>
@@ -678,10 +706,12 @@ defineExpose({form, questions, applyAiResult, generateLevel: useAiLevel})
:currentForm="form"
:confirmed="props.confirmed"
:members="props.members"
:steps="currentSteps"
@save="saveDraft"
@submit="submitConfirm"
@confirmTab="confirmTab"
@regenerate="onRegenerate"
@update:steps="updateSteps"
/>
<!-- 侧边抽屉:AI 类型判定 / 等级判定表 -->
<NDrawer :show="sheet !== ''" @update:show="(v) => !v && (sheet = '')" placement="right" :width="sheet === 'level' ? '45rem' : '28rem'">