|
|
|
|
@ -0,0 +1,405 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="app-container">
|
|
|
|
|
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="80px">
|
|
|
|
|
<el-form-item label="项目编号" prop="projectCode">
|
|
|
|
|
<el-input v-model="queryParams.projectCode" placeholder="请输入项目编号" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="项目名称" prop="projectName">
|
|
|
|
|
<el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="业务方向" prop="businessDirection">
|
|
|
|
|
<el-select v-model="queryParams.businessDirection" placeholder="请选择业务方向" clearable style="width: 200px">
|
|
|
|
|
<el-option v-for="dict in business_direction" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="项目状态" prop="projectStatus">
|
|
|
|
|
<el-select v-model="queryParams.projectStatus" placeholder="请选择项目状态" clearable style="width: 200px">
|
|
|
|
|
<el-option v-for="dict in project_status" :key="dict.value" :label="dict.label" :value="dict.value" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item>
|
|
|
|
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
|
|
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
|
|
|
<el-button type="warning" plain icon="Download" @click="handleExport">导出</el-button>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
<el-table v-loading="loading" :data="reportList" border @selection-change="handleSelectionChange">
|
|
|
|
|
<!-- ========== 基本信息 ========== -->
|
|
|
|
|
<el-table-column label="基本信息" align="center">
|
|
|
|
|
<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="客户名称" width="150" align="center" show-overflow-tooltip>
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._customerName || '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="项目名称" prop="projectName" width="200" align="center" show-overflow-tooltip />
|
|
|
|
|
<el-table-column label="项目经理" prop="managerName" width="90" align="center" />
|
|
|
|
|
<el-table-column label="部门" prop="deptName" width="120" align="center" show-overflow-tooltip />
|
|
|
|
|
<el-table-column label="项目类型" prop="typeName" width="120" align="center" show-overflow-tooltip />
|
|
|
|
|
<el-table-column label="产品型号" width="100" align="center">
|
|
|
|
|
<template #default>-</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="产品数量" width="100" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._productAmount != null ? scope.row._productAmount : '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="项目阶段" width="100" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<dict-tag v-if="scope.row._projectPhases" :options="project_phases" :value="scope.row._projectPhases" />
|
|
|
|
|
<span v-else>-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="情况说明" width="120" align="center">
|
|
|
|
|
<template #default>-</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="状态" width="80" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<dict-tag :options="project_status" :value="scope.row.projectStatus" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="预计验收时间" width="120" align="center">
|
|
|
|
|
<template #default>-</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="实际验收时间" width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._acceptanceDate || '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="是否异常启动" width="120" align="center">
|
|
|
|
|
<template #default>-</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="异常启动原因" width="120" align="center">
|
|
|
|
|
<template #default>-</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
<!-- ========== 合同信息 ========== -->
|
|
|
|
|
<el-table-column label="合同信息" align="center">
|
|
|
|
|
<el-table-column label="签订时间" width="110" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._contractDate || '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="合同额" width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._contractAmount != null ? formatNumber(scope.row._contractAmount) : '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="客户经理" width="90" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._contractManagerName || '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="付款方式" prop="paymentMethod" width="110" align="center" show-overflow-tooltip />
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
<!-- ========== 预算及成本 ========== -->
|
|
|
|
|
<el-table-column label="预算及成本" align="center">
|
|
|
|
|
<el-table-column label="预算" width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._budgetCost != null ? formatNumber(scope.row._budgetCost) : '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="预算毛利率" width="100" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._budgetRate != null ? scope.row._budgetRate + '%' : '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="降成本后预算" width="130" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._reduceBudgetCost != null ? formatNumber(scope.row._reduceBudgetCost) : '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="降成本后预算毛利率" width="150" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._reduceBudgetRate != null ? scope.row._reduceBudgetRate + '%' : '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="累计工时" width="100" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._totalHours != null ? scope.row._totalHours : '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="已发生成本" width="110" align="center">
|
|
|
|
|
<template #default>-</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="收入(合同额/1.13)" width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
{{ scope.row._revenue != null ? formatNumber(scope.row._revenue) : '-' }}
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="已发生成本占收入比例" width="160" align="center">
|
|
|
|
|
<template #default>-</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts" name="ProjectLedgerReport">
|
|
|
|
|
import { listProjectInfo } from '@/api/oa/erp/projectInfo';
|
|
|
|
|
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 { business_direction, project_status, project_phases } = toRefs<any>(proxy?.useDict('business_direction', 'project_status', 'project_phases'));
|
|
|
|
|
|
|
|
|
|
const loading = ref(true);
|
|
|
|
|
const showSearch = ref(true);
|
|
|
|
|
const total = ref(0);
|
|
|
|
|
const reportList = ref<any[]>([]);
|
|
|
|
|
const selectedRows = ref<any[]>([]);
|
|
|
|
|
const queryFormRef = ref<ElFormInstance>();
|
|
|
|
|
|
|
|
|
|
const queryParams = reactive<ProjectInfoQuery>({
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
pageSize: 20,
|
|
|
|
|
projectCode: undefined,
|
|
|
|
|
projectName: undefined,
|
|
|
|
|
businessDirection: undefined,
|
|
|
|
|
projectStatus: undefined,
|
|
|
|
|
params: {}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** 格式化数字 */
|
|
|
|
|
const formatNumber = (num: number) => {
|
|
|
|
|
return num?.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 查询报表列表 */
|
|
|
|
|
const getList = async () => {
|
|
|
|
|
loading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
// 1. 加载项目列表(排除合同订单类别 9)
|
|
|
|
|
const res = await listProjectInfo(queryParams);
|
|
|
|
|
const projects: any[] = (res.rows || []).filter((p: ProjectInfoVO) => p.projectCategory !== '9');
|
|
|
|
|
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 {
|
|
|
|
|
loading.value = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 搜索按钮操作 */
|
|
|
|
|
const handleQuery = () => {
|
|
|
|
|
queryParams.pageNum = 1;
|
|
|
|
|
getList();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 重置按钮操作 */
|
|
|
|
|
const resetQuery = () => {
|
|
|
|
|
queryFormRef.value?.resetFields();
|
|
|
|
|
handleQuery();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 多选框选中数据 */
|
|
|
|
|
const handleSelectionChange = (selection: any[]) => {
|
|
|
|
|
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 data = selectedRows.value.length > 0 ? selectedRows.value : reportList.value;
|
|
|
|
|
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' });
|
|
|
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
|
const a = document.createElement('a');
|
|
|
|
|
a.href = url;
|
|
|
|
|
a.download = `项目台账报表_${new Date().getTime()}.xls`;
|
|
|
|
|
document.body.appendChild(a);
|
|
|
|
|
a.click();
|
|
|
|
|
document.body.removeChild(a);
|
|
|
|
|
URL.revokeObjectURL(url);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
getList();
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
:deep(.el-table .el-table__header th) {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
</style>
|