refactor(projectLedgerReport): 重构项目台账报表数据获取逻辑

dev
Yangk 1 week ago
parent fcc2961aee
commit 5b8b4a9411

@ -29,9 +29,9 @@
<el-table-column label="基本信息" align="center"> <el-table-column label="基本信息" align="center">
<el-table-column type="selection" width="55" align="center" fixed="left" /> <el-table-column type="selection" width="55" align="center" fixed="left" />
<el-table-column label="项目编号" prop="projectCode" width="140" align="center" fixed="left" show-overflow-tooltip /> <el-table-column label="项目编号" prop="projectCode" width="140" align="center" fixed="left" show-overflow-tooltip />
<el-table-column label="客户名称" width="150" align="center" show-overflow-tooltip> <el-table-column label="客户名称" prop="customerName" width="150" align="center" show-overflow-tooltip>
<template #default="scope"> <template #default="scope">
{{ scope.row._customerName || '-' }} {{ scope.row.customerName || '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="项目名称" prop="projectName" width="200" align="center" show-overflow-tooltip /> <el-table-column label="项目名称" prop="projectName" width="200" align="center" show-overflow-tooltip />
@ -43,12 +43,12 @@
</el-table-column> </el-table-column>
<el-table-column label="产品数量" width="100" align="center"> <el-table-column label="产品数量" width="100" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._productAmount != null ? scope.row._productAmount : '-' }} {{ scope.row.productAmount != null ? scope.row.productAmount : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="项目阶段" width="100" align="center"> <el-table-column label="项目阶段" width="100" align="center">
<template #default="scope"> <template #default="scope">
<dict-tag v-if="scope.row._projectPhases" :options="project_phases" :value="scope.row._projectPhases" /> <dict-tag v-if="scope.row.projectPhases" :options="project_phases" :value="scope.row.projectPhases" />
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</el-table-column> </el-table-column>
@ -65,7 +65,7 @@
</el-table-column> </el-table-column>
<el-table-column label="实际验收时间" width="120" align="center"> <el-table-column label="实际验收时间" width="120" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._acceptanceDate || '-' }} {{ scope.row.acceptanceDate ? scope.row.acceptanceDate.substring(0, 10) : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="是否异常启动" width="120" align="center"> <el-table-column label="是否异常启动" width="120" align="center">
@ -80,17 +80,17 @@
<el-table-column label="合同信息" align="center"> <el-table-column label="合同信息" align="center">
<el-table-column label="签订时间" width="110" align="center"> <el-table-column label="签订时间" width="110" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._contractDate || '-' }} {{ scope.row.contractDate ? scope.row.contractDate.substring(0, 10) : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="合同额" width="120" align="center"> <el-table-column label="合同额" width="120" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._contractAmount != null ? formatNumber(scope.row._contractAmount) : '-' }} {{ scope.row.contractAmount != null ? formatNumber(scope.row.contractAmount) : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="客户经理" width="90" align="center"> <el-table-column label="客户经理" width="90" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._contractManagerName || '-' }} {{ scope.row.contractManagerName || '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="付款方式" prop="paymentMethod" width="110" align="center" show-overflow-tooltip /> <el-table-column label="付款方式" prop="paymentMethod" width="110" align="center" show-overflow-tooltip />
@ -100,27 +100,27 @@
<el-table-column label="预算及成本" align="center"> <el-table-column label="预算及成本" align="center">
<el-table-column label="预算" width="120" align="center"> <el-table-column label="预算" width="120" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._budgetCost != null ? formatNumber(scope.row._budgetCost) : '-' }} {{ scope.row.budgetCost != null ? formatNumber(scope.row.budgetCost) : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="预算毛利率" width="100" align="center"> <el-table-column label="预算毛利率" width="100" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._budgetRate != null ? scope.row._budgetRate + '%' : '-' }} {{ scope.row.budgetRate != null ? scope.row.budgetRate + '%' : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="降成本后预算" width="130" align="center"> <el-table-column label="降成本后预算" width="130" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._reduceBudgetCost != null ? formatNumber(scope.row._reduceBudgetCost) : '-' }} {{ scope.row.reduceBudgetCost != null ? formatNumber(scope.row.reduceBudgetCost) : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="降成本后预算毛利率" width="150" align="center"> <el-table-column label="降成本后预算毛利率" width="150" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._reduceBudgetRate != null ? scope.row._reduceBudgetRate + '%' : '-' }} {{ scope.row.reduceBudgetRate != null ? scope.row.reduceBudgetRate + '%' : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="累计工时" width="100" align="center"> <el-table-column label="累计工时" width="100" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._totalHours != null ? scope.row._totalHours : '-' }} {{ scope.row.totalHours != null ? scope.row.totalHours : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="已发生成本" width="110" align="center"> <el-table-column label="已发生成本" width="110" align="center">
@ -128,7 +128,7 @@
</el-table-column> </el-table-column>
<el-table-column label="收入(合同额/1.13" width="120" align="center"> <el-table-column label="收入(合同额/1.13" width="120" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row._revenue != null ? formatNumber(scope.row._revenue) : '-' }} {{ scope.row.revenue != null ? formatNumber(scope.row.revenue) : '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="已发生成本占收入比例" width="160" align="center"> <el-table-column label="已发生成本占收入比例" width="160" align="center">
@ -142,13 +142,7 @@
</template> </template>
<script setup lang="ts" name="ProjectLedgerReport"> <script setup lang="ts" name="ProjectLedgerReport">
import { listProjectInfo } from '@/api/oa/erp/projectInfo'; import { listProjectLedgerReport, ProjectLedgerReportQuery, ProjectLedgerReportVO } from '@/api/oa/erp/projectLedgerReport';
import { ProjectInfoVO, ProjectInfoQuery } from '@/api/oa/erp/projectInfo/types';
import { listErpBudgetInfo } from '@/api/oa/erp/budgetInfo';
import { listContractInfo } from '@/api/oa/erp/contractInfo';
import { listProjectAcceptance } from '@/api/oa/erp/projectAcceptance';
import { listProjectManHourReport } from '@/api/oa/erp/timesheetReport';
import { listContractMaterial } from '@/api/oa/erp/contractMaterial';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { business_direction, project_status, project_phases } = toRefs<any>(proxy?.useDict('business_direction', 'project_status', 'project_phases')); const { business_direction, project_status, project_phases } = toRefs<any>(proxy?.useDict('business_direction', 'project_status', 'project_phases'));
@ -156,18 +150,17 @@ const { business_direction, project_status, project_phases } = toRefs<any>(proxy
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
const total = ref(0); const total = ref(0);
const reportList = ref<any[]>([]); const reportList = ref<ProjectLedgerReportVO[]>([]);
const selectedRows = ref<any[]>([]); const selectedRows = ref<ProjectLedgerReportVO[]>([]);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const queryParams = reactive<ProjectInfoQuery>({ const queryParams = reactive<ProjectLedgerReportQuery>({
pageNum: 1, pageNum: 1,
pageSize: 20, pageSize: 20,
projectCode: undefined, projectCode: undefined,
projectName: undefined, projectName: undefined,
businessDirection: undefined, businessDirection: undefined,
projectStatus: undefined, projectStatus: undefined
params: {}
}); });
/** 格式化数字 */ /** 格式化数字 */
@ -179,115 +172,9 @@ const formatNumber = (num: number) => {
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
try { try {
// 1. 9 const res = await listProjectLedgerReport(queryParams);
const res = await listProjectInfo(queryParams); reportList.value = res.rows || [];
const projects: any[] = (res.rows || []).filter((p: ProjectInfoVO) => p.projectCategory !== '9');
total.value = res.total; total.value = res.total;
//
projects.forEach((p) => {
p._customerName = '';
p._contractDate = '';
p._contractAmount = null;
p._contractManagerName = '';
p._budgetCost = null;
p._budgetRate = null;
p._reduceBudgetCost = null;
p._reduceBudgetRate = null;
p._budgetContractAmount = null;
p._revenue = null;
p._acceptanceDate = '';
p._projectPhases = '';
p._totalHours = null;
p._productAmount = null;
});
// 2.
const [budgetRes, acceptanceRes, contractRes, manHourRes, materialRes] = await Promise.all([
listErpBudgetInfo({ pageNum: 1, pageSize: 9999, budgetStatus: '3' } as any).catch(() => ({ rows: [] })),
listProjectAcceptance({ pageNum: 1, pageSize: 9999 } as any).catch(() => ({ rows: [] })),
listContractInfo({ pageNum: 1, pageSize: 9999 } as any).catch(() => ({ rows: [] })),
listProjectManHourReport({ pageNum: 1, pageSize: 9999 } as any).catch(() => ({ data: [] })),
listContractMaterial({ pageNum: 1, pageSize: 9999 } as any).catch(() => ({ rows: [] }))
]);
//
const budgetMap = new Map<string, any>();
((budgetRes as any).rows || []).forEach((b: any) => {
if (b.projectId) budgetMap.set(String(b.projectId), b);
});
const acceptanceMap = new Map<string, any>();
((acceptanceRes as any).rows || []).forEach((a: any) => {
if (a.projectId && a.flowStatus === 'finish') {
acceptanceMap.set(String(a.projectId), a);
}
});
const contractMap = new Map<string, any>();
((contractRes as any).rows || []).forEach((c: any) => {
if (c.contractId) contractMap.set(String(c.contractId), c);
});
const manHourMap = new Map<string, number>();
const manHourData = (manHourRes as any).data || (manHourRes as any).rows || [];
manHourData.forEach((m: any) => {
if (m.projectId) {
const pid = String(m.projectId);
manHourMap.set(pid, (manHourMap.get(pid) || 0) + Number(m.totalHours || 0));
}
});
// contractId amount
const materialAmountMap = new Map<string, number>();
((materialRes as any).rows || []).forEach((m: any) => {
if (m.contractId) {
const cid = String(m.contractId);
materialAmountMap.set(cid, (materialAmountMap.get(cid) || 0) + Number(m.amount || 0));
}
});
// 3.
projects.forEach((p) => {
const pid = String(p.projectId);
//
if (p.contractId) {
const contract = contractMap.get(String(p.contractId));
if (contract) {
p._customerName = contract.oneCustomerName || '';
p._contractDate = contract.contractDate || '';
p._contractManagerName = contract.contractManagerName || '';
}
//
const amt = materialAmountMap.get(String(p.contractId));
if (amt != null) {
p._productAmount = amt;
}
}
//
const budget = budgetMap.get(pid);
if (budget) {
p._budgetCost = budget.budgetCost;
p._budgetRate = budget.budgetRate;
p._reduceBudgetCost = budget.reduceBudgetCost;
p._reduceBudgetRate = budget.reduceBudgetRate;
p._budgetContractAmount = budget.contractAmount;
p._revenue = budget.contractAmount != null ? budget.contractAmount / 1.13 : null;
p._contractAmount = budget.contractAmount;
}
//
const acceptance = acceptanceMap.get(pid);
if (acceptance) {
p._acceptanceDate = acceptance.acceptanceDate || '';
}
//
const hours = manHourMap.get(pid);
if (hours != null) {
p._totalHours = hours;
}
});
reportList.value = projects;
} finally { } finally {
loading.value = false; loading.value = false;
} }
@ -306,91 +193,20 @@ const resetQuery = () => {
}; };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: any[]) => { const handleSelectionChange = (selection: ProjectLedgerReportVO[]) => {
selectedRows.value = selection; selectedRows.value = selection;
}; };
/** 获取字典标签 */
const getDictLabel = (dictOptions: any, value: string | number | undefined) => {
if (value == null || value === '') return '-';
const opts = dictOptions?.value || dictOptions || [];
const item = opts.find((d: any) => String(d.value) === String(value));
return item?.label || '-';
};
/** 导出按钮操作 */ /** 导出按钮操作 */
const handleExport = () => { const handleExport = () => {
const data = selectedRows.value.length > 0 ? selectedRows.value : reportList.value; const exportParams: any = { ...queryParams };
if (!data || data.length === 0) {
proxy?.$modal.msgWarning('没有可导出的数据');
return;
}
// HTML table
const html = `
<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">
<head><meta charset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>
<x:Name>项目台账报表</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions>
</x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body>
<table border="1" cellspacing="0" cellpadding="4" style="border-collapse:collapse; font-size:11px;">
<tr>
<td colspan="16" align="center" style="font-weight:bold; background:#CCE5FF;">基本信息</td>
<td colspan="4" align="center" style="font-weight:bold; background:#FFCCCC;">合同信息</td>
<td colspan="8" align="center" style="font-weight:bold; background:#CCFFCC;">预算及成本</td>
</tr>
<tr style="font-weight:bold; font-size:10px;">
<td>序号</td><td>项目编号</td><td>客户名称</td><td>项目名称</td><td>项目经理</td>
<td>部门</td><td>项目类型</td><td>产品型号</td><td>产品数量</td><td>项目阶段</td><td>情况说明</td>
<td>状态</td><td>预计验收时间</td><td>实际验收时间</td>
<td>是否异常启动</td><td>异常启动原因</td>
<td>签订时间</td><td>合同额</td><td>客户经理</td><td>付款方式</td>
<td>预算</td><td>预算毛利率</td><td>降成本后预算</td>
<td>降成本后预算毛利率</td><td>已发生成本</td><td>收入合同额/1.13</td><td>已发生成本占收入比例</td><td>累计工时</td>
</tr>
${data
.map(
(row: any, idx: number) => `<tr>
<td>${idx + 1}</td>
<td>${row.projectCode || '-'}</td>
<td>${row._customerName || '-'}</td>
<td>${row.projectName || '-'}</td>
<td>${row.managerName || '-'}</td>
<td>${row.deptName || '-'}</td>
<td>${row.typeName || '-'}</td>
<td>-</td>
<td>${row._productAmount != null ? row._productAmount : '-'}</td>
<td>${row._projectPhases ? getDictLabel(project_phases, row._projectPhases) : '-'}</td>
<td>-</td>
<td>${getDictLabel(project_status, row.projectStatus)}</td>
<td>-</td>
<td>${row._acceptanceDate || '-'}</td>
<td>-</td>
<td>-</td>
<td>${row._contractDate || '-'}</td>
<td>${row._contractAmount != null ? formatNumber(row._contractAmount) : '-'}</td>
<td>${row._contractManagerName || '-'}</td>
<td>${row.paymentMethod || '-'}</td>
<td>${row._budgetCost != null ? formatNumber(row._budgetCost) : '-'}</td>
<td>${row._budgetRate != null ? row._budgetRate + '%' : '-'}</td>
<td>${row._reduceBudgetCost != null ? formatNumber(row._reduceBudgetCost) : '-'}</td>
<td>${row._reduceBudgetRate != null ? row._reduceBudgetRate + '%' : '-'}</td>
<td>-</td>
<td>${row._revenue != null ? formatNumber(row._revenue) : '-'}</td>
<td>-</td>
<td>${row._totalHours != null ? row._totalHours : '-'}</td>
</tr>`
)
.join('')}
</table></body></html>`;
const blob = new Blob([html], { type: 'application/vnd.ms-excel;charset=utf-8' }); // params.projectIds
const url = URL.createObjectURL(blob); if (selectedRows.value.length > 0) {
const a = document.createElement('a'); exportParams['params[projectIds]'] = selectedRows.value.map((row) => row.projectId).join(',');
a.href = url; }
a.download = `项目台账报表_${new Date().getTime()}.xls`;
document.body.appendChild(a); proxy?.download('/oa/erp/projectLedgerReport/export', exportParams, `项目台账报表_${new Date().getTime()}.xlsx`);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}; };
onMounted(() => { onMounted(() => {

@ -5,9 +5,7 @@
<el-input v-model="queryParams.projectCode" placeholder="请输入项目编号" clearable style="width: 240px" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.projectCode" placeholder="请输入项目编号" clearable style="width: 240px" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="项目名称" prop="projectName"> <el-form-item label="项目名称" prop="projectName">
<el-select v-model="queryParams.projectName" placeholder="请输入项目名称" filterable clearable style="width: 240px" @change="handleQuery"> <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
<el-option v-for="item in projectOptions" :key="item.projectId" :label="item.projectName" :value="item.projectName" />
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="部门" prop="deptName"> <el-form-item label="部门" prop="deptName">
<el-select v-model="queryParams.deptName" placeholder="请输入部门名称" filterable clearable style="width: 240px" @change="handleQuery"> <el-select v-model="queryParams.deptName" placeholder="请输入部门名称" filterable clearable style="width: 240px" @change="handleQuery">

Loading…
Cancel
Save