feat(M08-B): 补保洁统计导出

This commit is contained in:
Codex
2026-07-06 14:43:19 +08:00
parent 6c977632c0
commit a067c1d020
6 changed files with 91 additions and 10 deletions
@@ -37,6 +37,7 @@
</el-form-item>
<el-button type="primary" :icon="Search" @click="emitFilter">查询</el-button>
</el-form>
<el-button :icon="Download" @click="exportStatistics">导出</el-button>
<el-tooltip content="刷新统计">
<el-button :icon="RefreshCw" :loading="loading" circle @click="$emit('refresh')" />
</el-tooltip>
@@ -162,7 +163,7 @@
<script setup lang="ts">
import { computed, reactive, watch } from 'vue';
import { RefreshCw, Search } from '@lucide/vue';
import { Download, RefreshCw, Search } from '@lucide/vue';
import { compactText, money } from '../format';
import type { CleaningStatistics, ManagedUser } from '../types';
@@ -230,4 +231,72 @@ function formatDate(date: Date) {
function trendWidth(total: number) {
return `${Math.max(6, Math.round((total / maxTrendTotal.value) * 100))}%`;
}
function exportStatistics() {
downloadCsv(`cleaning-statistics-${Date.now()}.csv`, [
['section', 'key', 'value', 'extra1', 'extra2', 'extra3', 'extra4'],
['filter', 'from', filterDraft.from || '未设置', '', '', '', ''],
['filter', 'to', filterDraft.to || '未设置', '', '', '', ''],
['filter', 'storeId', filterDraft.storeId || '全部门店', '', '', '', ''],
['filter', 'cleanerUserId', filterDraft.cleanerUserId || '全部保洁员', '', '', '', ''],
['summary', '任务总数', String(props.statistics.summary.taskTotal), '', '', '', ''],
['summary', '待验收', String(props.statistics.summary.pendingReview), '', '', '', ''],
['summary', '进行中', String(props.statistics.summary.active), '', '', '', ''],
['summary', '驳回', String(props.statistics.summary.rejected), '', '', '', ''],
['summary', '已完成', String(props.statistics.summary.completed), '', '', '', ''],
['summary', '奖励合计', money(props.statistics.summary.rewardCents), '', '', '', ''],
['summary', '待结算', money(props.statistics.summary.pendingSettlementCents), '', '', '', ''],
['summary', '待发放', money(props.statistics.summary.confirmedSettlementCents), '', '', '', ''],
['summary', '已发放', money(props.statistics.summary.paidSettlementCents), '', '', '', ''],
['任务状态', '状态', '数量', '奖励', '', '', ''],
...props.statistics.byStatus.map((row) => ['任务状态', row.status, String(row.total), money(row.rewardCents), '', '', '']),
['结算口径', '状态', '单数', '金额', '', '', ''],
...props.statistics.settlements.map((row) => ['结算口径', row.status, String(row.total), money(row.rewardCents), '', '', '']),
['每日趋势', '日期', '任务', '待验', '完成', '驳回', '已发放'],
...props.statistics.trend.map((row) => [
'每日趋势',
row.date,
String(row.taskTotal),
String(row.pendingReview),
String(row.completed),
String(row.rejected),
money(row.paidSettlementCents)
]),
['门店明细', '门店', '任务', '待验', '完成', '驳回', '奖励'],
...props.statistics.byStore.map((row) => [
'门店明细',
`${row.storeName || ''}(${row.storeId})`,
String(row.taskTotal),
String(row.pendingReview),
String(row.completed),
String(row.rejected),
money(row.rewardCents)
]),
['成员分账', '保洁员', '任务', '待结算', '已结算', '', ''],
...props.statistics.members.map((row) => [
'成员分账',
`${row.cleanerName || ''}(${row.cleanerUserId})`,
String(row.taskCount),
money(row.pendingSettlementCents),
money(row.settledRewardCents),
'',
''
])
]);
}
function downloadCsv(filename: string, rows: string[][]) {
const content = rows.map((row) => row.map(csvCell).join(',')).join('\r\n');
const blob = new Blob([`\uFEFF${content}`], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
function csvCell(value: string) {
return `"${String(value ?? '').replace(/"/g, '""')}"`;
}
</script>