|
|
|
|
@ -177,6 +177,10 @@
|
|
|
|
|
<el-tooltip v-if="scope.row.projectChangeStatus === '2' || scope.row.projectChangeStatus === '3'" content="审批记录" placement="top">
|
|
|
|
|
<el-button link type="info" icon="DocumentCopy" @click="handleApprovalRecord(scope.row)"></el-button>
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
<!-- 历史变更记录按钮 -->
|
|
|
|
|
<el-tooltip content="查看历史变更记录" placement="top">
|
|
|
|
|
<el-button link type="warning" icon="Clock" @click="handleViewHistory(scope.row)"></el-button>
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
<!-- 删除按钮:草稿、驳回或撤回状态显示 -->
|
|
|
|
|
<el-tooltip
|
|
|
|
|
v-if="scope.row.projectChangeStatus === '1' || scope.row.flowStatus === 'back' || scope.row.flowStatus === 'cancel'"
|
|
|
|
|
@ -199,11 +203,34 @@
|
|
|
|
|
</el-card>
|
|
|
|
|
<!-- 审批记录 -->
|
|
|
|
|
<ApprovalRecord ref="approvalRecordRef" />
|
|
|
|
|
|
|
|
|
|
<!-- 项目变更历史记录抽屉 -->
|
|
|
|
|
<el-drawer v-model="historyDrawer.visible" :title="historyDrawer.title" size="700px">
|
|
|
|
|
<el-timeline v-if="historyDrawer.list && historyDrawer.list.length > 0">
|
|
|
|
|
<el-timeline-item
|
|
|
|
|
v-for="(item, index) in historyDrawer.list"
|
|
|
|
|
:key="index"
|
|
|
|
|
:timestamp="`第 ${item.changeNumber} 次变更 (${parseTime(item.createTime, '{y}-{m}-{d} {h}:{i}')} 申请人: ${item.createName || '未知'})`"
|
|
|
|
|
placement="top"
|
|
|
|
|
type="primary"
|
|
|
|
|
size="large"
|
|
|
|
|
>
|
|
|
|
|
<el-card shadow="hover">
|
|
|
|
|
<div v-for="(msg, mIndex) in item.messages" :key="mIndex" style="margin-bottom: 15px; font-size: 14px; line-height: 1.5;">
|
|
|
|
|
<el-tag :type="msg.type === '预算变更' ? 'success' : 'warning'" size="small" style="margin-right: 10px;" effect="dark">{{ msg.type }}</el-tag>
|
|
|
|
|
<span style="color: #606266;">{{ msg.desc }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="item.messages.length === 0" style="color: #909399; font-size: 13px;">本次变更未修改预算金额与进度计划。</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-timeline-item>
|
|
|
|
|
</el-timeline>
|
|
|
|
|
<el-empty v-else description="暂无历史变更记录数据" />
|
|
|
|
|
</el-drawer>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup name="ErpProjectChange" lang="ts">
|
|
|
|
|
import { delErpProjectChange, listErpProjectChange } from '@/api/oa/erp/erpProjectChange';
|
|
|
|
|
import { delErpProjectChange, listErpProjectChange, getProjectChangeHistory } from '@/api/oa/erp/erpProjectChange';
|
|
|
|
|
import { ErpProjectChangeQuery, ErpProjectChangeVO } from '@/api/oa/erp/erpProjectChange/types';
|
|
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
|
import { onActivated } from 'vue';
|
|
|
|
|
@ -215,8 +242,8 @@ const router = useRouter();
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
|
|
|
|
const refreshFlagKey = 'erpProjectChangeListShouldRefresh';
|
|
|
|
|
const { active_flag, change_type, project_change_status, project_category, wf_business_status } = toRefs<any>(
|
|
|
|
|
proxy?.useDict('active_flag', 'change_type', 'project_change_status', 'project_category', 'wf_business_status')
|
|
|
|
|
const { active_flag, change_type, project_change_status, project_category, wf_business_status, project_phases } = toRefs<any>(
|
|
|
|
|
proxy?.useDict('active_flag', 'change_type', 'project_change_status', 'project_category', 'wf_business_status', 'project_phases')
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 项目变更列表数据(表格数据源)
|
|
|
|
|
@ -395,6 +422,126 @@ const handleApprovalRecord = (row: ErpProjectChangeVO) => {
|
|
|
|
|
approvalRecordRef.value?.init(row.projectChangeId);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ================ 历史变更记录查看相关 ================
|
|
|
|
|
const historyDrawer = reactive({
|
|
|
|
|
visible: false,
|
|
|
|
|
title: '',
|
|
|
|
|
list: [] as any[]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const handleViewHistory = async (row: any) => {
|
|
|
|
|
historyDrawer.title = `每一次变更的历史记录(${row.projectName})`;
|
|
|
|
|
historyDrawer.visible = true;
|
|
|
|
|
historyDrawer.list = [];
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
proxy?.$modal.loading('正在提取变更历史记录...');
|
|
|
|
|
const res = await getProjectChangeHistory(row.projectId);
|
|
|
|
|
if (res.data) {
|
|
|
|
|
const records = res.data;
|
|
|
|
|
const historyList = [];
|
|
|
|
|
|
|
|
|
|
const previousBudget: Record<string, number> = {};
|
|
|
|
|
const previousProgressContent: Record<string, any> = {};
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < records.length; i++) {
|
|
|
|
|
const item = records[i];
|
|
|
|
|
const messages: any[] = [];
|
|
|
|
|
|
|
|
|
|
// 1. 预算变更详情提取
|
|
|
|
|
if (item.budgetList && item.budgetList.length > 0) {
|
|
|
|
|
item.budgetList.forEach((budget: any) => {
|
|
|
|
|
const after = Number(budget.budgetAfter || 0);
|
|
|
|
|
const prev = previousBudget[budget.subjectName];
|
|
|
|
|
// 第一次变更的变更前预算直接取本次自身的数据
|
|
|
|
|
const before = prev !== undefined ? prev : Number(budget.budgetBefore || 0);
|
|
|
|
|
|
|
|
|
|
if (before !== after) {
|
|
|
|
|
messages.push({
|
|
|
|
|
type: '预算变更',
|
|
|
|
|
desc: `将预算变更中的【${budget.subjectName}】从变更前预算的 ${before.toFixed(2)} 变为变更后预算的 ${after.toFixed(2)}`
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 记录该科目本次变更的最后状态,供下一次比对
|
|
|
|
|
previousBudget[budget.subjectName] = after;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 进度变更详情提取
|
|
|
|
|
if (item.progressList && item.progressList.length > 0) {
|
|
|
|
|
item.progressList.forEach((progress: any) => {
|
|
|
|
|
const phaseKey = progress.projectPhases;
|
|
|
|
|
const prev = previousProgressContent[phaseKey] || {
|
|
|
|
|
start: progress.originalStart,
|
|
|
|
|
end: progress.originalEnd,
|
|
|
|
|
milestone: ''
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const diffs = [];
|
|
|
|
|
let startChanged = false;
|
|
|
|
|
let endChanged = false;
|
|
|
|
|
|
|
|
|
|
// 对比开始时间
|
|
|
|
|
if (prev.start && progress.changedStart && prev.start !== progress.changedStart) {
|
|
|
|
|
diffs.push(`原计划时间起的 ${proxy?.parseTime(prev.start, '{y}-{m}-{d}')} 变为变更后时间起的 ${proxy?.parseTime(progress.changedStart, '{y}-{m}-{d}')}`);
|
|
|
|
|
startChanged = true;
|
|
|
|
|
}
|
|
|
|
|
// 对比结束时间
|
|
|
|
|
if (prev.end && progress.changedEnd && prev.end !== progress.changedEnd) {
|
|
|
|
|
diffs.push(`原计划时间止的 ${proxy?.parseTime(prev.end, '{y}-{m}-{d}')} 变为变更后时间止的 ${proxy?.parseTime(progress.changedEnd, '{y}-{m}-{d}')}`);
|
|
|
|
|
endChanged = true;
|
|
|
|
|
}
|
|
|
|
|
// 里程碑变动情况
|
|
|
|
|
let msDiff = '';
|
|
|
|
|
// 判断是否是该阶段首次填入或与上一次填写的里程碑名不一致
|
|
|
|
|
if (progress.milestoneName && prev.milestone !== progress.milestoneName) {
|
|
|
|
|
const oldName = prev.milestone ? `“${prev.milestone}”` : '空白';
|
|
|
|
|
msDiff = `里程碑名称从原来的${oldName}变为“${progress.milestoneName}”`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (diffs.length > 0 || msDiff) {
|
|
|
|
|
const phaseLabel = proxy?.selectDictLabel(project_phases.value, progress.projectPhases) || progress.projectPhases || '未知';
|
|
|
|
|
const diffStr = diffs.join(',');
|
|
|
|
|
let finalDescText = diffStr;
|
|
|
|
|
|
|
|
|
|
if (finalDescText && msDiff) {
|
|
|
|
|
finalDescText += `,同时将${msDiff}`;
|
|
|
|
|
} else if (msDiff) {
|
|
|
|
|
finalDescText = `将${msDiff}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
messages.push({
|
|
|
|
|
type: '进度变更',
|
|
|
|
|
desc: `将进度变更中【${phaseLabel}】阶段的 ` + finalDescText
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新本次阶段的最终状态供后续比较,如果没有更改则继承上次的值(如果在本次记录中这些字段为空的话)
|
|
|
|
|
previousProgressContent[phaseKey] = {
|
|
|
|
|
start: startChanged ? progress.changedStart : prev.start,
|
|
|
|
|
end: endChanged ? progress.changedEnd : prev.end,
|
|
|
|
|
milestone: progress.milestoneName
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
historyList.push({
|
|
|
|
|
changeNumber: item.changeNumber,
|
|
|
|
|
createTime: item.createTime,
|
|
|
|
|
createName: item.createName || item.projectManagerName,
|
|
|
|
|
messages: messages
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
historyDrawer.list = historyList;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("加载变更历史记录失败", error);
|
|
|
|
|
} finally {
|
|
|
|
|
proxy?.$modal.closeLoading();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
// ========================================================
|
|
|
|
|
|
|
|
|
|
/** 删除按钮操作 */
|
|
|
|
|
const handleDelete = async (row?: ErpProjectChangeVO) => {
|
|
|
|
|
const _projectChangeIds = row?.projectChangeId || ids.value;
|
|
|
|
|
|