|
|
|
|
@ -3,32 +3,16 @@
|
|
|
|
|
<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>
|
|
|
|
|
<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>
|
|
|
|
|
@ -44,22 +28,28 @@
|
|
|
|
|
<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-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-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-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-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-input v-model="form.chargeName" placeholder="自动带出" disabled />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
@ -80,6 +70,16 @@
|
|
|
|
|
</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" />
|
|
|
|
|
@ -99,77 +99,110 @@
|
|
|
|
|
<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-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"/>
|
|
|
|
|
<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"/>
|
|
|
|
|
<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-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"/>
|
|
|
|
|
<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"/>
|
|
|
|
|
<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)"/>
|
|
|
|
|
<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)"/>
|
|
|
|
|
<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">
|
|
|
|
|
<!-- <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%"
|
|
|
|
|
/>
|
|
|
|
|
<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%"
|
|
|
|
|
/>
|
|
|
|
|
<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"/>
|
|
|
|
|
<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">
|
|
|
|
|
@ -177,8 +210,72 @@
|
|
|
|
|
<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-${changeIndex}`"
|
|
|
|
|
:label="`第${changeIndex + 1}次变更`"
|
|
|
|
|
header-align="center"
|
|
|
|
|
align="center"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column label="原起" min-width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
|
|
|
|
|
{{ getChangeProgressByStage(change, scope.row.planStageId).originalStart || '-' }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="text-gray-400">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="原止" min-width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
|
|
|
|
|
{{ getChangeProgressByStage(change, scope.row.planStageId).originalEnd || '-' }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="text-gray-400">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="变更起" min-width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
|
|
|
|
|
{{ getChangeProgressByStage(change, scope.row.planStageId).changedStart || '-' }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="text-gray-400">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="变更止" min-width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
|
|
|
|
|
{{ getChangeProgressByStage(change, scope.row.planStageId).changedEnd || '-' }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="text-gray-400">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="里程碑" min-width="140" 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-400">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="完成度(%)" min-width="110" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<span v-if="getChangeProgressByStage(change, scope.row.planStageId)" class="change-highlight">
|
|
|
|
|
{{ getChangeProgressByStage(change, scope.row.planStageId).completionDegree ?? '-' }}
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else class="text-gray-400">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="备注" min-width="160" align="left">
|
|
|
|
|
<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-400">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table-column>-->
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
</el-form>
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
@ -186,15 +283,31 @@
|
|
|
|
|
<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 { ref, reactive, onMounted, onActivated, getCurrentInstance, watch, computed } from 'vue';
|
|
|
|
|
import { computed, getCurrentInstance, onActivated, onMounted, reactive, ref, watch } from 'vue';
|
|
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
|
import { getErpProjectPlan, addErpProjectPlan, updateErpProjectPlan, projectPlanSubmitAndFlowStart } from '@/api/oa/erp/erpProjectPlan';
|
|
|
|
|
import { addErpProjectPlan, getErpProjectPlan, projectPlanSubmitAndFlowStart, updateErpProjectPlan } from '@/api/oa/erp/erpProjectPlan';
|
|
|
|
|
import { ErpProjectPlanForm } from '@/api/oa/erp/erpProjectPlan/types';
|
|
|
|
|
import { listUser } from '@/api/system/user';
|
|
|
|
|
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';
|
|
|
|
|
@ -202,8 +315,8 @@ 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 { 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();
|
|
|
|
|
@ -249,10 +362,7 @@ const isBasicEditable = computed(() => {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 草稿、驳回、撤回均允许项目经理完整编辑
|
|
|
|
|
return (
|
|
|
|
|
form.value.projectPlanStatus === '1' ||
|
|
|
|
|
['back', 'cancel'].includes(form.value.flowStatus as string)
|
|
|
|
|
);
|
|
|
|
|
return form.value.projectPlanStatus === '1' || ['back', 'cancel'].includes(form.value.flowStatus as string);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 计算属性:判断是否可编辑延迟/备注等有限字段
|
|
|
|
|
@ -284,11 +394,7 @@ const showLimitedActions = computed(() => !isViewMode.value && isLimitedMode.val
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
) {
|
|
|
|
|
if (approvalPageType.value === 'update' && form.value.flowStatus && ['draft', 'cancel', 'back'].includes(form.value.flowStatus as string)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
@ -308,19 +414,35 @@ 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 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: []
|
|
|
|
|
});
|
|
|
|
|
@ -347,17 +469,11 @@ const getProjectInfoList = async () => {
|
|
|
|
|
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()]);
|
|
|
|
|
await getProjectInfoList();
|
|
|
|
|
baseDataLoaded.value = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -399,10 +515,7 @@ interface ProjectSyncOptions {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 处理项目选择变化,自动填充项目经理、部门负责人、付款方式和项目编号 */
|
|
|
|
|
const handleProjectChange = (
|
|
|
|
|
projectId?: string | number,
|
|
|
|
|
{ syncManagerAndCharge = true, syncPaymentMethod = true }: ProjectSyncOptions = {}
|
|
|
|
|
) => {
|
|
|
|
|
const handleProjectChange = (projectId?: string | number, { syncManagerAndCharge = true, syncPaymentMethod = true }: ProjectSyncOptions = {}) => {
|
|
|
|
|
const isEmptySelection = projectId === undefined || projectId === null || projectId === '';
|
|
|
|
|
if (isEmptySelection) {
|
|
|
|
|
if (syncManagerAndCharge) {
|
|
|
|
|
@ -416,7 +529,7 @@ const handleProjectChange = (
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const project = projectInfoList.value.find(item => String(item.projectId) === String(projectId));
|
|
|
|
|
const project = projectInfoList.value.find((item) => String(item.projectId) === String(projectId));
|
|
|
|
|
if (!project) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@ -424,16 +537,21 @@ const handleProjectChange = (
|
|
|
|
|
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;
|
|
|
|
|
@ -454,6 +572,38 @@ const recalcReceivableDate = (row: any) => {
|
|
|
|
|
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') => {
|
|
|
|
|
@ -484,14 +634,14 @@ const submitForm = async (status = 'draft') => {
|
|
|
|
|
try {
|
|
|
|
|
// 更新阶段的projectId
|
|
|
|
|
if (form.value.planStageList) {
|
|
|
|
|
form.value.planStageList.forEach(stage => {
|
|
|
|
|
form.value.planStageList.forEach((stage) => {
|
|
|
|
|
stage.projectId = form.value.projectId;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提交审批
|
|
|
|
|
if (status === 'submit') {
|
|
|
|
|
const project = projectInfoList.value.find(p => p.projectId === form.value.projectId);
|
|
|
|
|
const project = projectInfoList.value.find((p) => p.projectId === form.value.projectId);
|
|
|
|
|
form.value.flowCode = 'OAPS';
|
|
|
|
|
form.value.variables = {
|
|
|
|
|
projectId: form.value.projectId,
|
|
|
|
|
@ -570,7 +720,7 @@ const saveLimited = () => {
|
|
|
|
|
buttonLoading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
if (form.value.planStageList) {
|
|
|
|
|
form.value.planStageList.forEach(stage => {
|
|
|
|
|
form.value.planStageList.forEach((stage) => {
|
|
|
|
|
stage.projectId = form.value.projectId;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@ -590,6 +740,60 @@ 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 [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 构建变更进度的Map,key为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();
|
|
|
|
|
@ -615,9 +819,14 @@ const loadFormData = async () => {
|
|
|
|
|
syncPaymentMethod: false
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 在查看模式下加载变更记录
|
|
|
|
|
if (isViewMode.value) {
|
|
|
|
|
await loadProjectChangeList();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
resetForm();
|
|
|
|
|
form.value.flowStatus = 'draft';
|
|
|
|
|
projectChangeList.value = [];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -658,4 +867,56 @@ onActivated(() => {
|
|
|
|
|
.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>
|
|
|
|
|
|