feat(erp): 实现项目变更审批流程功能

- 集成审批按钮组件,支持提交、审批记录查看等功能
- 添加表单编辑状态控制,区分查看、审批、编辑模式- 实现预算和进度表格的可- 优化日期编辑性控制选择器和输入框的禁用状态逻辑
- 调整路由配置,支持通过query参数传递变更申请ID
-回调处理和数据 增加审批加载逻辑
- 完善错误提示和加载状态管理- 修复缺少必要参数时的错误提示信息
dev
zangch@mesnac.com 1 month ago
parent 3f0abdc86a
commit 2b79e3c512

@ -124,11 +124,17 @@ export const constantRoutes: RouteRecordRaw[] = [
meta: { title: '项目计划甘特图', activeMenu: '/oa/erp/erpProjectPlan' }
},
{
path: 'erpProjectChange/edit/:projectChangeId',
path: 'erpProjectChange/edit',
component: () => import('@/views/oa/erp/erpProjectChange/edit.vue'),
name: 'ErpProjectChangeEdit',
meta: { title: '项目变更编辑', activeMenu: '/oa/erp/erpProjectPlan' }
meta: { title: '项目变更', activeMenu: '/oa/erp/erpProjectPlan' }
},
{
path: 'erpProjectChange/edit/:projectChangeId',
component: () => import('@/views/oa/erp/erpProjectChange/edit.vue'),
name: 'ErpProjectChangeEditById',
meta: { title: '项目变更编辑', activeMenu: '/oa/erp/erpProjectPlan' }
}
]
},
{

@ -1,17 +1,19 @@
<template>
<div class="p-2">
<el-card shadow="never">
<div class="mb-3 flex items-center justify-between">
<div>
<el-button type="info" :loading="draftLoading" :disabled="submitLoading" @click="handleSave('draft')"></el-button>
<el-button type="primary" :loading="submitLoading" :disabled="draftLoading" @click="handleSave('submit')"></el-button>
</div>
<div>
<el-button @click="goBack"></el-button>
</div>
</div>
<approvalButton
@submitForm="handleApprovalSubmit"
@approvalVerifyOpen="approvalVerifyOpen"
@handleApprovalRecord="handleApprovalRecord"
:buttonLoading="buttonLoading"
:id="form.projectChangeId ?? ''"
:status="form.flowStatus ?? 'draft'"
:pageType="approvalPageType"
:mode="false"
/>
<el-form ref="projectChangeFormRef" :model="form" :rules="rules" label-width="140px">
<!-- 基本信息 -->
<el-divider content-position="left">基本信息</el-divider>
<el-row :gutter="20">
<el-col :span="12">
@ -53,12 +55,19 @@
</el-col>
<el-col :span="12">
<el-form-item label="申请变更日期" prop="applyChangeDate">
<el-date-picker v-model="form.applyChangeDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择申请变更日期" style="width: 100%"/>
<el-date-picker
v-model="form.applyChangeDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择申请变更日期"
:disabled="!isFormEditable"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="变更类型" prop="changeType">
<el-checkbox-group v-model="changeTypeList">
<el-checkbox-group v-model="changeTypeList" :disabled="!isFormEditable">
<el-checkbox v-for="dict in change_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
@ -78,7 +87,7 @@
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-row v-if="isFormEditable" :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" icon="Plus" @click="handleAddBudget"></el-button>
</el-col>
@ -87,35 +96,35 @@
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="科目名称" width="200" align="center">
<template #default="scope">
<el-input v-model="scope.row.subjectName" placeholder="请输入科目名称" />
<el-input v-model="scope.row.subjectName" placeholder="请输入科目名称" :disabled="!isFormEditable" />
</template>
</el-table-column>
<el-table-column label="变更前预算" width="150" align="center">
<template #default="scope">
<el-input-number v-model="scope.row.budgetBefore" :min="0" :precision="2" controls-position="right" style="width: 100%" />
<el-input-number v-model="scope.row.budgetBefore" :min="0" :precision="2" controls-position="right" :disabled="!isFormEditable" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="变更后预算" width="150" align="center">
<template #default="scope">
<el-input-number v-model="scope.row.budgetAfter" :min="0" :precision="2" controls-position="right" style="width: 100%" />
<el-input-number v-model="scope.row.budgetAfter" :min="0" :precision="2" controls-position="right" :disabled="!isFormEditable" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="已使用金额" width="150" align="center">
<template #default="scope">
<el-input-number v-model="scope.row.amountUsed" :min="0" :precision="2" controls-position="right" style="width: 100%" />
<el-input-number v-model="scope.row.amountUsed" :min="0" :precision="2" controls-position="right" :disabled="!isFormEditable" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="费用调整原因" min-width="200" align="center">
<template #default="scope">
<el-input v-model="scope.row.adjustmentReason" placeholder="请输入费用调整原因" />
<el-input v-model="scope.row.adjustmentReason" placeholder="请输入费用调整原因" :disabled="!isFormEditable" />
</template>
</el-table-column>
<el-table-column label="备注" min-width="150" align="center">
<template #default="scope">
<el-input v-model="scope.row.remark" placeholder="请输入备注" />
<el-input v-model="scope.row.remark" placeholder="请输入备注" :disabled="!isFormEditable" />
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center" fixed="right">
<el-table-column v-if="isFormEditable" label="操作" width="80" align="center" fixed="right">
<template #default="scope">
<el-button type="danger" link icon="Delete" @click="handleDeleteBudget(scope.$index)"></el-button>
</template>
@ -132,7 +141,7 @@
</el-table-column>
<el-table-column label="里程碑名称" width="150" align="center">
<template #default="scope">
<el-input v-model="scope.row.milestoneName" placeholder="请输入里程碑名称" />
<el-input v-model="scope.row.milestoneName" placeholder="请输入里程碑名称" :disabled="!isFormEditable" />
</template>
</el-table-column>
<el-table-column label="原计划时间起" width="160" align="center">
@ -147,22 +156,22 @@
</el-table-column>
<el-table-column label="变更后时间起" width="160" align="center">
<template #default="scope">
<el-date-picker v-model="scope.row.changedStart" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 100%" />
<el-date-picker v-model="scope.row.changedStart" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" :disabled="!isFormEditable" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="变更后时间止" width="160" align="center">
<template #default="scope">
<el-date-picker v-model="scope.row.changedEnd" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 100%" />
<el-date-picker v-model="scope.row.changedEnd" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" :disabled="!isFormEditable" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="里程碑完成程度(%)" width="180" align="center">
<el-table-column label="完成程度" width="120" align="center">
<template #default="scope">
<el-input-number v-model="scope.row.completionDegree" :min="0" :max="100" :precision="2" controls-position="right" style="width: 100%" />
<el-input-number v-model="scope.row.completionDegree" :min="0" :max="100" :precision="0" controls-position="right" :disabled="!isFormEditable" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="备注" min-width="200" align="center">
<template #default="scope">
<el-input v-model="scope.row.remark" placeholder="请输入备注" />
<el-input v-model="scope.row.remark" placeholder="请输入备注" :disabled="!isFormEditable" />
</template>
</el-table-column>
</el-table>
@ -170,34 +179,40 @@
<el-divider content-position="left">项目当前情况说明</el-divider>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="当前情况说明" prop="currentStatus">
<el-input v-model="form.currentStatus" type="textarea" :rows="3" placeholder="请输入当前情况说明" />
<el-form-item label="当前情况" prop="currentStatus">
<el-input v-model="form.currentStatus" type="textarea" :rows="3" placeholder="请输入当前情况" :disabled="!isFormEditable" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="变更原因" prop="changeReason">
<el-input v-model="form.changeReason" type="textarea" :rows="3" placeholder="请输入变更原因" />
<el-input v-model="form.changeReason" type="textarea" :rows="3" placeholder="请输入变更原因" :disabled="!isFormEditable" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="后续工作" prop="followUpWork">
<el-input v-model="form.followUpWork" type="textarea" :rows="3" placeholder="请输入后续工作" />
<el-input v-model="form.followUpWork" type="textarea" :rows="3" placeholder="请输入后续工作" :disabled="!isFormEditable" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
<approvalRecord ref="approvalRecordRef" />
</div>
</template>
<script setup lang="ts" name="ErpProjectChangeEdit">
import { ref, reactive, onMounted, getCurrentInstance, toRefs, watch, onActivated, nextTick } from 'vue';
import { ref, reactive, onMounted, getCurrentInstance, toRefs, watch, onActivated, nextTick, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { getErpProjectChange, addErpProjectChange, updateErpProjectChange, prepareProjectChangeWithInfo, submitProjectChangeAndFlowStart } from '@/api/oa/erp/erpProjectChange';
import { ErpProjectChangeForm, ErpProjectChangeBudget, ErpProjectChangeProgress } from '@/api/oa/erp/erpProjectChange/types';
import { useUserStore } from '@/store/modules/user';
import type { FormInstance as ElFormInstance } from 'element-plus';
import type { ComponentInternalInstance } from 'vue';
import SubmitVerify from '@/components/Process/submitVerify.vue';
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
import ApprovalButton from '@/components/Process/approvalButton.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { project_category, change_type, project_phases } = toRefs<any>(proxy?.useDict('project_category', 'change_type', 'project_phases'));
@ -209,6 +224,28 @@ const projectChangeFormRef = ref<ElFormInstance>();
const draftLoading = ref(false);
const submitLoading = ref(false);
const changeTypeList = ref<string[]>([]);
const routeParams = ref<any>({
...route.params,
...route.query
});
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
const taskVariables = ref<Record<string, any>>({});
//
const isViewMode = computed(() => routeParams.value.type === 'view');
//
const isApprovalMode = computed(() => routeParams.value.type === 'approval');
//
const isFormEditable = computed(() => !isViewMode.value && !isApprovalMode.value);
const buttonLoading = computed(() => draftLoading.value || submitLoading.value);
const approvalPageType = computed(() => {
const type = routeParams.value.type as string | undefined;
if (!type) {
return form.value.projectChangeId ? 'update' : 'add';
}
return type === 'edit' ? 'update' : type;
});
const formatToday = () => (proxy?.parseTime ? proxy.parseTime(new Date(), '{y}-{m}-{d}') : new Date().toISOString().slice(0, 10));
@ -241,6 +278,7 @@ const getDefaultForm = (): ErpProjectChangeForm => ({
changeReason: '',
followUpWork: '',
projectChangeStatus: '1',
flowStatus: 'draft',
activeFlag: '1',
budgetList: [],
progressList: []
@ -375,13 +413,65 @@ const goBack = () => {
router.back();
};
const handleApprovalSubmit = async (status: string, _mode: boolean) => {
if (status === 'draft' || status === 'submit') {
await handleSave(status as 'draft' | 'submit');
}
};
const approvalVerifyOpen = async () => {
const taskId = routeParams.value.taskId as string | undefined;
await submitVerifyRef.value?.openDialog(taskId);
};
const handleApprovalRecord = () => {
if (form.value.projectChangeId) {
approvalRecordRef.value?.init(form.value.projectChangeId);
}
};
const submitCallback = async () => {
await loadFormData();
};
const loadFormData = async () => {
await resetFormState();
//
routeParams.value = {
...route.params,
...route.query
};
// 使 query.id/使 params.projectChangeId
const id = routeParams.value.id as string | number;
const projectChangeId = route.params.projectChangeId as string;
const projectId = route.query.projectId as string;
if (projectChangeId && projectChangeId !== '0') {
//
// //
if (id && (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval')) {
// // id
try {
proxy?.$modal.loading('正在加载数据,请稍后...');
const res = await getErpProjectChange(id);
if (res.data) {
Object.assign(form.value, res.data);
const dateOnly = toDateOnly(form.value.applyChangeDate);
form.value.applyChangeDate = dateOnly ?? formatToday();
form.value.budgetList = res.data.budgetList ?? [];
form.value.progressList = res.data.progressList ?? [];
if (res.data.changeType) {
changeTypeList.value = res.data.changeType.split(',');
}
}
proxy?.$modal.closeLoading();
} catch (error) {
proxy?.$modal.closeLoading();
proxy?.$modal.msgError('加载变更申请数据失败');
console.error(error);
}
} else if (projectChangeId && projectChangeId !== '0') {
//
try {
const res = await getErpProjectChange(projectChangeId);
if (res.data) {
@ -402,7 +492,7 @@ const loadFormData = async () => {
// ID
await loadProjectDataByProjectId(projectId);
} else {
proxy?.$modal.msgError('缺少必要的参数项目ID');
proxy?.$modal.msgError('缺少必要的参数项目ID或变更申请ID');
}
};

Loading…
Cancel
Save