|
|
|
|
|
<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="managerId">
|
|
|
|
|
|
<el-select v-model="form.managerId" placeholder="请选择项目经理" filterable disabled>
|
|
|
|
|
|
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId"/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
|
<el-form-item label="部门负责人" prop="chargeId">
|
|
|
|
|
|
<el-select v-model="form.chargeId" placeholder="请选择部门负责人" filterable disabled>
|
|
|
|
|
|
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId"/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</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="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>
|
|
|
|
|
|
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 提交审批组件 -->
|
|
|
|
|
|
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
|
|
|
|
|
|
<!-- 审批记录 -->
|
|
|
|
|
|
<approvalRecord ref="approvalRecordRef" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts" name="ErpProjectPlanEdit">
|
|
|
|
|
|
import { ref, reactive, onMounted, onActivated, getCurrentInstance, watch, computed } from 'vue';
|
|
|
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
|
|
import { getErpProjectPlan, addErpProjectPlan, updateErpProjectPlan, projectPlanSubmitAndFlowStart } from '@/api/oa/erp/erpProjectPlan';
|
|
|
|
|
|
import { ErpProjectPlanForm } from '@/api/oa/erp/erpProjectPlan/types';
|
|
|
|
|
|
import { listUser } from '@/api/system/user';
|
|
|
|
|
|
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';
|
|
|
|
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
|
const { project_plan_status , project_phases, collection_stage } = toRefs<any>(
|
|
|
|
|
|
proxy?.useDict('project_plan_status', 'project_phases', 'collection_stage')
|
|
|
|
|
|
);
|
|
|
|
|
|
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 userList = ref<any[]>([]);
|
|
|
|
|
|
const selectedProjectCode = ref<string>(''); // 展示选中项目的编号
|
|
|
|
|
|
const baseDataLoaded = ref(false);
|
|
|
|
|
|
|
|
|
|
|
|
const createEmptyForm = (): ErpProjectPlanForm => ({
|
|
|
|
|
|
projectPlanId: undefined,
|
|
|
|
|
|
projectId: undefined,
|
|
|
|
|
|
managerId: undefined,
|
|
|
|
|
|
chargeId: undefined,
|
|
|
|
|
|
paymentMethod: undefined,
|
|
|
|
|
|
projectPlanStatus: '1',
|
|
|
|
|
|
flowStatus: 'draft',
|
|
|
|
|
|
contractId: 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 getUserList = async () => {
|
|
|
|
|
|
const res = await listUser({ pageNum: 1, pageSize: 9999 });
|
|
|
|
|
|
userList.value = res.rows;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const initBaseData = async () => {
|
|
|
|
|
|
if (baseDataLoaded.value) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
await Promise.all([getProjectInfoList(), getUserList()]);
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (syncPaymentMethod) {
|
|
|
|
|
|
form.value.paymentMethod = (project.paymentMethod as string) ?? undefined;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
selectedProjectCode.value = (project.projectCode 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 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 = 'OAPS';
|
|
|
|
|
|
form.value.variables = {
|
|
|
|
|
|
projectId: form.value.projectId,
|
|
|
|
|
|
projectName: project?.projectName,
|
|
|
|
|
|
managerId: form.value.managerId
|
|
|
|
|
|
};
|
|
|
|
|
|
form.value.bizExt = {
|
|
|
|
|
|
businessTitle: '项目计划审批',
|
|
|
|
|
|
businessCode: project?.projectName
|
|
|
|
|
|
};
|
|
|
|
|
|
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);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
resetForm();
|
|
|
|
|
|
form.value.flowStatus = 'draft';
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** 返回列表 */
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|