You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

912 lines
32 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="p-2">
<el-card shadow="never">
<div v-if="showApprovalButton" class="mb-3 flex items-center justify-between">
<div>
<el-button v-if="showSubmitButtons" type="info" :loading="draftLoading" :disabled="submitLoading" @click="handleSubmitAction('draft')"
>暂存</el-button
>
<el-button v-if="showSubmitButtons" type="primary" :loading="submitLoading" :disabled="draftLoading" @click="handleSubmitAction('submit')"
>提交</el-button
>
<el-button v-if="showApprovalAction" type="primary" :disabled="draftLoading || submitLoading" @click="approvalVerifyOpen">审批</el-button>
<el-button v-if="showProgressButton" type="primary" :disabled="draftLoading || submitLoading" @click="handleApprovalRecord"
>流程进度</el-button
>
</div>
<div>
<el-button @click="goBack">返回</el-button>
</div>
</div>
<div v-else-if="showLimitedActions" class="mb-3 text-right">
<el-button type="primary" :loading="buttonLoading" @click="handleSave">保存</el-button>
<el-button @click="goBack">返回</el-button>
</div>
<el-form ref="projectPlanFormRef" :model="form" :rules="rules" :disabled="isViewMode" label-width="120px">
<el-divider content-position="left">基本信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目名称" prop="projectId">
<el-select v-model="form.projectId" placeholder="请选择项目" filterable :disabled="!isBasicEditable" @change="handleProjectChange">
<el-option v-for="item in projectInfoList" :key="item.projectId" :label="item.projectName" :value="item.projectId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目计划编号" prop="projectPlanCode">
<el-input v-model="form.projectPlanCode" placeholder="由系统自动生成" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目经理" prop="managerId">
<el-input v-model="form.managerName" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目经理部门">
<el-input v-model="form.managerDeptName" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门负责人" prop="chargeId">
<el-input v-model="form.chargeName" placeholder="自动带出" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目编号">
<el-input v-model="selectedProjectCode" placeholder="选择项目后自动显示" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="付款方式" prop="paymentMethod">
<el-input v-model="form.paymentMethod" placeholder="3-3-3-1" :disabled="!isBasicEditable" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目计划状态" prop="projectPlanStatus">
<!-- <el-select v-model="form.projectPlanStatus" placeholder="请选择项目计划状态" :disabled="!isBasicEditable"> -->
<el-select v-model="form.projectPlanStatus" placeholder="请选择项目计划状态" :disabled="true">
<el-option v-for="dict in project_plan_status" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="附件" prop="ossId">
<template v-if="isViewMode">
<el-button type="primary" plain icon="View" @click="handlePreview" :disabled="!form.ossId">预览附件</el-button>
</template>
<template v-else>
<el-button type="primary" plain icon="Upload" @click="handleFile">上传附件</el-button>
</template>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" :disabled="!isBasicEditable" />
</el-form-item>
</el-col>
</el-row>
<el-divider content-position="left">项目阶段计划</el-divider>
<el-row :gutter="10" class="mb8" v-if="isBasicEditable">
<el-col :span="1.5">
<el-button type="primary" icon="Plus" @click="handleAddStage">添加阶段</el-button>
</el-col>
</el-row>
<el-table :data="form.planStageList" border stripe max-height="600">
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="项目阶段" width="150" align="center">
<template #default="scope">
<el-select v-model="scope.row.projectPhases" placeholder="请选择项目阶段" style="width: 100%" :disabled="!isBasicEditable">
<el-option v-for="dict in project_phases" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</template>
</el-table-column>
<el-table-column label="计划开始时间" width="160" align="center">
<template #default="scope">
<el-date-picker
v-model="scope.row.planStartTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择日期"
style="width: 100%"
:disabled="!isBasicEditable"
/>
</template>
</el-table-column>
<el-table-column label="计划结束时间" width="160" align="center">
<template #default="scope">
<el-date-picker
v-model="scope.row.planEndTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择日期"
style="width: 100%"
:disabled="!isBasicEditable"
/>
</template>
</el-table-column>
<el-table-column label="回款阶段" width="150" align="center">
<template #default="scope">
<el-select v-model="scope.row.collectionStage" placeholder="请选择回款阶段" style="width: 100%" :disabled="!isBasicEditable">
<el-option v-for="dict in collection_stage" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</template>
</el-table-column>
<el-table-column label="回款比例(%)" width="120" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.repaymentRate"
:min="0"
:max="100"
:precision="2"
controls-position="right"
style="width: 100%"
:disabled="!isBasicEditable"
/>
</template>
</el-table-column>
<el-table-column label="预计回款金额" width="140" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.repaymentAmount"
:min="0"
:precision="2"
controls-position="right"
style="width: 100%"
:disabled="!isBasicEditable"
/>
</template>
</el-table-column>
<el-table-column label="预计回款时间" width="160" align="center">
<template #default="scope">
<el-date-picker
v-model="scope.row.repaymentTime"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择日期"
style="width: 100%"
:disabled="!isBasicEditable"
@change="recalcReceivableDate(scope.row)"
/>
</template>
</el-table-column>
<el-table-column label="回款延期天数" width="140" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.delayDay"
:min="0"
:step="1"
controls-position="right"
style="width: 100%"
:disabled="!canEditLimited"
@change="recalcReceivableDate(scope.row)"
/>
</template>
</el-table-column>
<!-- <el-table-column label="应收款日期" width="160" align="center">
<template #default="scope">
<el-date-picker v-model="scope.row.receivableDate" type="date" value-format="YYYY-MM-DD" placeholder="自动计算" style="width: 100%" disabled/>
</template>
</el-table-column>-->
<el-table-column label="实际开始" min-width="140">
<template #default="scope">
<el-date-picker v-model="scope.row.realStartTime" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="实际结束" min-width="140">
<template #default="scope">
<el-date-picker v-model="scope.row.realEndTime" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="进度备注" width="200" align="center">
<template #default="scope">
<el-input v-model="scope.row.scheduleRemark" placeholder="请输入进度备注" :disabled="!canEditLimited" />
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center" fixed="right" v-if="isBasicEditable">
<template #default="scope">
<el-button type="danger" link icon="Delete" @click="handleDeleteStage(scope.$index)">删除</el-button>
</template>
</el-table-column>
<!-- 动态变更列(多列合并展示) -->
<el-table-column
v-for="(change, changeIndex) in projectChangeList"
:key="`change-${change.projectChangeId || changeIndex}`"
:label="`第${change.changeNumber || changeIndex + 1}次变更`"
align="center"
>
<el-table-column label="变更开始" width="110" align="center">
<template #default="scope">
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
{{ (getChangeProgressByStage(change, scope.row.planStageId).changedStart || '').substring(0, 10) || '-' }}
</span>
<span v-else class="text-gray-300">-</span>
</template>
</el-table-column>
<el-table-column label="变更结束" width="110" align="center">
<template #default="scope">
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
{{ (getChangeProgressByStage(change, scope.row.planStageId).changedEnd || '').substring(0, 10) || '-' }}
</span>
<span v-else class="text-gray-300">-</span>
</template>
</el-table-column>
<el-table-column label="里程碑" width="100" align="center">
<template #default="scope">
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
{{ getChangeProgressByStage(change, scope.row.planStageId).milestoneName || '-' }}
</span>
<span v-else class="text-gray-300">-</span>
</template>
</el-table-column>
<el-table-column label="完成度" width="80" align="center">
<template #default="scope">
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
{{
getChangeProgressByStage(change, scope.row.planStageId).completionDegree != null
? getChangeProgressByStage(change, scope.row.planStageId).completionDegree + '%'
: '-'
}}
</span>
<span v-else class="text-gray-300">-</span>
</template>
</el-table-column>
<el-table-column label="备注" width="120" align="center" show-overflow-tooltip>
<template #default="scope">
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
{{ getChangeProgressByStage(change, scope.row.planStageId).remark || '-' }}
</span>
<span v-else class="text-gray-300">-</span>
</template>
</el-table-column>
</el-table-column>
</el-table>
</el-form>
</el-card>
<!-- 提交审批组件 -->
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
<!-- 审批记录 -->
<approvalRecord ref="approvalRecordRef" />
<!-- 附件上传/预览对话框 -->
<el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
<el-form label-width="80px">
<el-form-item label="文件名">
<fileUpload v-if="type === 0" v-model="ossFileModel" :disabled="isViewMode" />
<imageUpload v-if="type === 1" v-model="ossFileModel" :disabled="isViewMode" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button v-if="!isViewMode" type="primary" @click="submitOss">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="ErpProjectPlanEdit">
import { computed, getCurrentInstance, onActivated, onMounted, reactive, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { addErpProjectPlan, getErpProjectPlan, projectPlanSubmitAndFlowStart, updateErpProjectPlan } from '@/api/oa/erp/erpProjectPlan';
import { ErpProjectPlanForm } from '@/api/oa/erp/erpProjectPlan/types';
import { queryProjectChangeByProjectPlanId } from '@/api/oa/erp/erpProjectChange';
import { getErpProjectInfoList } from '@/api/oa/erp/projectInfo';
import type { ProjectInfoVO } from '@/api/oa/erp/projectInfo/types';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import { useUserStore } from '@/store/modules/user';
import { FlowCodeEnum } from '@/enums/OAEnum';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { project_plan_status, project_phases, collection_stage, project_change_status } = toRefs<any>(
proxy?.useDict('project_plan_status', 'project_phases', 'collection_stage', 'project_change_status')
);
const route = useRoute();
const router = useRouter();
const userStore = useUserStore();
const refreshFlagKey = 'erpProjectPlanListShouldRefresh';
// 审批相关组件引用
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
// 路由参数
const routeParams = ref<any>({});
const syncRouteParams = () => {
const projectPlanId = (route.params.projectPlanId as string) || (route.query.id as string);
const type = (route.query.type as string) || (route.params.type as string) || 'edit';
const taskId = (route.query.taskId as string) || (route.params.taskId as string);
routeParams.value = {
projectPlanId,
type,
taskId
};
};
syncRouteParams();
// approvalButton组件的pageType将'edit'转换为'update'
const approvalPageType = computed(() => {
if (routeParams.value.type === 'edit') {
return 'update';
}
return routeParams.value.type;
});
// 是否只读模式
const isViewMode = computed(() => ['view', 'approval'].includes(routeParams.value.type));
// 流程相关数据
const taskVariables = ref<any>({});
// 计算属性:判断基础信息是否可编辑
const isBasicEditable = computed(() => {
if (isViewMode.value) {
return false;
}
// 草稿、驳回、撤回均允许项目经理完整编辑
return form.value.projectPlanStatus === '1' || ['back', 'cancel'].includes(form.value.flowStatus as string);
});
// 计算属性:判断是否可编辑延迟/备注等有限字段
const canEditLimited = computed(() => {
if (isViewMode.value) {
return false;
}
if (isBasicEditable.value) {
return true;
}
// 审批完成允许维护延期与进度备注
return form.value.projectPlanStatus === '3' || form.value.flowStatus === 'finish';
});
// 限制模式(审批完成,仅维护部分字段)
const isLimitedMode = computed(() => !isBasicEditable.value && canEditLimited.value);
// 按钮展示控制
const showApprovalButton = computed(() => {
if (isLimitedMode.value) {
return false;
}
if (routeParams.value.type === 'approval') {
return true;
}
return !isViewMode.value;
});
const showLimitedActions = computed(() => !isViewMode.value && isLimitedMode.value);
const showSubmitButtons = computed(() => {
if (approvalPageType.value === 'add') return true;
if (approvalPageType.value === 'update' && form.value.flowStatus && ['draft', 'cancel', 'back'].includes(form.value.flowStatus as string)) {
return true;
}
return false;
});
const showApprovalAction = computed(() => {
return approvalPageType.value === 'approval' && form.value.flowStatus === 'waiting';
});
const showProgressButton = computed(() => {
return !!form.value.projectPlanId && form.value.flowStatus !== 'draft';
});
const projectPlanFormRef = ref<ElFormInstance>();
const buttonLoading = ref(false);
const draftLoading = ref(false);
const submitLoading = ref(false);
const currentAction = ref<'draft' | 'submit' | null>(null);
const projectInfoList = ref<Partial<ProjectInfoVO>[]>([]);
const selectedProjectCode = ref<string>(''); // 展示选中项目的编号
const baseDataLoaded = ref(false);
// 项目计划变更记录相关
const projectChangeList = ref<any[]>([]); // 变更记录列表
const activeChangeTab = ref<string>('0'); // 当前活跃的变更标签
// 附件上传对话框状态
const type = ref(0);
const dialog = reactive<{ visible: boolean; title: string }>({
visible: false,
title: ''
});
const ossFileModel = ref<string | string[] | undefined>(undefined);
const createEmptyForm = (): ErpProjectPlanForm => ({
projectPlanId: undefined,
projectId: undefined,
managerId: undefined,
chargeId: undefined,
managerName: undefined,
chargeName: undefined,
managerDeptName: undefined,
projectPlanCode: undefined,
paymentMethod: undefined,
projectPlanStatus: '1',
flowStatus: 'draft',
contractId: undefined,
ossId: undefined,
remark: undefined,
planStageList: []
});
const form = ref<ErpProjectPlanForm>(createEmptyForm());
const rules = reactive({
projectId: [{ required: true, message: '请选择项目', trigger: 'change' }],
managerId: [{ required: true, message: '请选择项目经理', trigger: 'change' }]
});
const notifyListRefresh = () => {
sessionStorage.setItem(refreshFlagKey, Date.now().toString());
};
const resetForm = () => {
form.value = createEmptyForm();
selectedProjectCode.value = '';
};
/** 获取项目列表 */
const getProjectInfoList = async () => {
const res = await getErpProjectInfoList({});
projectInfoList.value = (res.data || []) as Partial<ProjectInfoVO>[];
};
const initBaseData = async () => {
if (baseDataLoaded.value) {
return;
}
await getProjectInfoList();
baseDataLoaded.value = true;
};
/** 添加阶段 */
const handleAddStage = () => {
const newStage = {
planStageId: undefined,
projectId: form.value.projectId,
projectPlanId: form.value.projectPlanId,
projectPhases: undefined,
planStartTime: undefined,
planEndTime: undefined,
collectionStage: undefined,
repaymentRate: undefined,
repaymentAmount: undefined,
repaymentTime: undefined,
delayDay: undefined,
receivableDate: undefined,
reasonsExplanation: undefined,
scheduleRemark: undefined,
realStartTime: undefined,
realEndTime: undefined,
sortOrder: (form.value.planStageList?.length || 0) + 1
};
if (!form.value.planStageList) {
form.value.planStageList = [];
}
form.value.planStageList.push(newStage);
};
/** 删除阶段 */
const handleDeleteStage = (index: number) => {
form.value.planStageList?.splice(index, 1);
};
interface ProjectSyncOptions {
syncManagerAndCharge?: boolean;
syncPaymentMethod?: boolean;
}
/** 处理项目选择变化,自动填充项目经理、部门负责人、付款方式和项目编号 */
const handleProjectChange = (projectId?: string | number, { syncManagerAndCharge = true, syncPaymentMethod = true }: ProjectSyncOptions = {}) => {
const isEmptySelection = projectId === undefined || projectId === null || projectId === '';
if (isEmptySelection) {
if (syncManagerAndCharge) {
form.value.managerId = undefined;
form.value.chargeId = undefined;
}
if (syncPaymentMethod) {
form.value.paymentMethod = undefined;
}
selectedProjectCode.value = '';
return;
}
const project = projectInfoList.value.find((item) => String(item.projectId) === String(projectId));
if (!project) {
return;
}
if (syncManagerAndCharge) {
form.value.managerId = project.managerId ?? undefined;
form.value.chargeId = project.chargeId ?? undefined;
// 名称字段从后端项目信息连表数据带出
form.value.managerName = ((project as any).managerName as string) ?? '';
form.value.chargeName = ((project as any).chargeName as string) ?? '';
}
if (syncPaymentMethod) {
form.value.paymentMethod = (project.paymentMethod as string) ?? undefined;
}
// 项目编号与项目经理部门均从后端项目信息中带出
selectedProjectCode.value = (project.projectCode as string) || '';
// ProjectInfoVO中deptName来源于后端连表ErpProjectInfoVo.deptName
form.value.managerDeptName = ((project as any).deptName as string) ?? '';
};
/** 根据回款时间与延期天数生成应收款日期 */
const recalcReceivableDate = (row: any) => {
if (!row) return;
const time = row.repaymentTime;
const delay = Number(row.delayDay || 0);
if (!time) {
row.receivableDate = undefined;
return;
}
const base = new Date(time as string);
if (isNaN(base.getTime())) {
row.receivableDate = undefined;
return;
}
const target = new Date(base.getTime());
target.setDate(target.getDate() + delay);
// 使用系统已有的parseTime进行格式化保持与全局一致
row.receivableDate = proxy?.parseTime ? proxy.parseTime(target, '{y}-{m}-{d}') : target.toISOString().slice(0, 10);
};
// 打开附件上传对话框(编辑模式)
const handleFile = () => {
type.value = 0;
dialog.visible = true;
dialog.title = '上传项目计划附件';
// 回显已有附件
ossFileModel.value = form.value.ossId as any;
};
// 查看模式下预览附件
const handlePreview = () => {
if (!form.value.ossId) {
proxy?.$modal.msgWarning('暂无附件可预览');
return;
}
type.value = 0;
dialog.visible = true;
dialog.title = '预览项目计划附件';
ossFileModel.value = form.value.ossId as any;
};
// 提交附件
const submitOss = () => {
form.value.ossId = ossFileModel.value as any;
dialog.visible = false;
proxy?.$modal.msgSuccess('附件已更新');
};
// 关闭附件对话框
const cancel = () => {
dialog.visible = false;
};
/** 提交表单 */
const submitForm = async (status = 'draft') => {
currentAction.value = status as 'draft' | 'submit';
if (currentAction.value === 'draft') {
draftLoading.value = true;
} else if (currentAction.value === 'submit') {
submitLoading.value = true;
}
// 权限校验:只有项目经理才能提交(超级管理员跳过校验)
if (!form.value.managerId) {
proxy?.$modal.msgError('请先选择项目');
resetActionLoading();
return;
}
// 超级管理员跳过权限校验
const isSuperAdmin = userStore.roles.includes('admin') || userStore.roles.includes('superadmin');
if (!isSuperAdmin && userStore.userId !== form.value.managerId) {
proxy?.$modal.msgError('只有项目经理才能提交项目计划');
resetActionLoading();
return;
}
projectPlanFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
try {
// 更新阶段的projectId
if (form.value.planStageList) {
form.value.planStageList.forEach((stage) => {
stage.projectId = form.value.projectId;
});
}
// 提交审批
if (status === 'submit') {
const project = projectInfoList.value.find((p) => p.projectId === form.value.projectId);
form.value.flowCode = FlowCodeEnum.PROJECT_PLAN_CODE; //OAPS
form.value.variables = {
projectId: form.value.projectId,
projectName: project?.projectName,
managerId: form.value.managerId
};
// 流程实例业务扩展字段:编码使用项目计划编号,标题使用项目名称
form.value.bizExt = {
businessTitle: project?.projectName,
businessCode: form.value.projectPlanCode
};
form.value.projectPlanStatus = '2';
form.value.flowStatus = 'waiting';
const res = await projectPlanSubmitAndFlowStart(form.value).finally(() => (buttonLoading.value = false));
form.value = res.data;
buttonLoading.value = false;
proxy?.$modal.msgSuccess('操作成功');
notifyListRefresh();
proxy?.$tab.closePage();
router.go(-1);
} else {
// 暂存
if (status === 'draft') {
form.value.projectPlanStatus = '1';
form.value.flowStatus = 'draft';
}
if (form.value.projectPlanId) {
await updateErpProjectPlan(form.value).finally(() => (buttonLoading.value = false));
} else {
await addErpProjectPlan(form.value).finally(() => (buttonLoading.value = false));
}
buttonLoading.value = false;
proxy?.$modal.msgSuccess('暂存成功');
notifyListRefresh();
proxy?.$tab.closePage();
router.go(-1);
}
} finally {
buttonLoading.value = false;
resetActionLoading();
}
} else {
resetActionLoading();
buttonLoading.value = false;
}
});
};
const handleSubmitAction = (type: 'draft' | 'submit') => {
if (draftLoading.value || submitLoading.value) return;
submitForm(type);
};
const resetActionLoading = () => {
if (currentAction.value === 'draft') {
draftLoading.value = false;
}
if (currentAction.value === 'submit') {
submitLoading.value = false;
}
currentAction.value = null;
};
/** 保存按钮 - 保存为草稿 */
const handleSave = () => {
if (isLimitedMode.value) {
saveLimited();
} else {
submitForm('draft');
}
};
/** 审批完成后的保存逻辑 */
const saveLimited = () => {
projectPlanFormRef.value?.validate(async (valid: boolean) => {
if (!valid) return;
buttonLoading.value = true;
try {
if (form.value.planStageList) {
form.value.planStageList.forEach((stage) => {
stage.projectId = form.value.projectId;
});
}
await updateErpProjectPlan(form.value);
proxy?.$modal.msgSuccess('保存成功');
notifyListRefresh();
proxy?.$tab.closePage();
router.go(-1);
} finally {
buttonLoading.value = false;
}
});
};
/** 审批对话框 */
const approvalVerifyOpen = async () => {
await submitVerifyRef.value?.openDialog(routeParams.value.taskId);
};
/**
* 获取指定阶段在某个变更中的进度信息
* @param change 变更记录
* @param planStageId 项目阶段ID
*/
const getChangeProgressByStage = (change: any, planStageId: any) => {
if (!change.progressList || change.progressList.length === 0) {
return null;
}
return change.progressList.find((progress: any) => progress.planStageId === planStageId);
};
/**
* 获取对齐的进度变更数据
* 将项目阶段与变更进度对齐显示
*/
const getAlignedProgressData = (change: any) => {
if (!form.value.planStageList || form.value.planStageList.length === 0) {
return [];
}
// 构建变更进度的Mapkey为planStageId
const progressMap = new Map();
if (change.progressList && change.progressList.length > 0) {
change.progressList.forEach((progress: any) => {
progressMap.set(progress.planStageId, progress);
});
}
// 将项目阶段与对应的变更进度合并
return form.value.planStageList.map((stage: any) => ({
...stage,
changeProgress: progressMap.get(stage.planStageId)
}));
};
/**
* 加载项目计划的变更记录
*/
const loadProjectChangeList = async () => {
if (!form.value.projectPlanId) {
projectChangeList.value = [];
return;
}
try {
const res = await queryProjectChangeByProjectPlanId(form.value.projectPlanId);
projectChangeList.value = res.data || [];
activeChangeTab.value = '0'; // 重置为第一个标签
} catch (error) {
projectChangeList.value = [];
console.error('加载项目计划变更记录失败:', error);
}
};
const loadFormData = async () => {
await initBaseData();
syncRouteParams();
const projectPlanId = routeParams.value.projectPlanId;
draftLoading.value = false;
submitLoading.value = false;
buttonLoading.value = false;
if (projectPlanId && projectPlanId !== '0') {
const res = await getErpProjectPlan(projectPlanId as string);
form.value = res.data;
if (!form.value.flowStatus) {
form.value.flowStatus = 'draft';
}
if (!form.value.planStageList) {
form.value.planStageList = [];
}
if (form.value.projectId) {
handleProjectChange(form.value.projectId, {
syncManagerAndCharge: false,
syncPaymentMethod: false
});
}
// 在查看模式或审批完成状态下加载变更记录
if (isViewMode.value || form.value.projectPlanStatus === '3') {
await loadProjectChangeList();
}
} else {
resetForm();
form.value.flowStatus = 'draft';
projectChangeList.value = [];
}
};
/** 返回列表 */
const goBack = () => {
router.back();
};
/** 审批记录 */
const handleApprovalRecord = () => {
approvalRecordRef.value?.init(form.value.projectPlanId);
};
// 提交回调
const submitCallback = async () => {
notifyListRefresh();
await proxy?.$tab.closePage(route);
router.go(-1);
};
watch(
() => route.fullPath,
() => {
loadFormData();
}
);
onMounted(() => {
loadFormData();
});
onActivated(() => {
loadFormData();
});
</script>
<style scoped lang="scss">
.el-divider {
margin: 20px 0;
}
//
.change-cell {
padding: 8px;
background-color: #fffacd;
border-radius: 4px;
font-size: 12px;
.change-item {
margin-bottom: 6px;
display: flex;
align-items: center;
&:last-child {
margin-bottom: 0;
}
.label {
font-weight: 500;
color: #333;
margin-right: 8px;
min-width: 60px;
}
.value {
color: #666;
word-break: break-word;
}
}
}
.change-highlight {
display: inline-block;
padding: 2px 6px;
background-color: #fffacd;
border-radius: 3px;
color: #333;
}
.info-item {
margin-bottom: 12px;
.label {
font-weight: 500;
color: #333;
margin-right: 8px;
}
.value {
color: #666;
}
}
</style>