feat(M08-B): 补保洁统计明细

This commit is contained in:
Codex
2026-06-30 10:24:39 +08:00
parent f59beb0e75
commit e48d1f0301
15 changed files with 616 additions and 46 deletions
+68 -23
View File
@@ -53,19 +53,19 @@
<section class="metric-grid" aria-label="保洁概览">
<div class="metric">
<span>待验收</span>
<strong>{{ metrics.submitted }}</strong>
<strong>{{ statistics.summary.pendingReview }}</strong>
</div>
<div class="metric">
<span>已完成</span>
<strong>{{ metrics.completed }}</strong>
<strong>{{ statistics.summary.completed }}</strong>
</div>
<div class="metric">
<span>待发放</span>
<strong>{{ metrics.confirmed }}</strong>
<strong>{{ money(statistics.summary.confirmedSettlementCents) }}</strong>
</div>
<div class="metric">
<span>本页金额</span>
<strong>{{ money(metrics.amountCents) }}</strong>
<span>待结算</span>
<strong>{{ money(statistics.summary.pendingSettlementCents) }}</strong>
</div>
</section>
@@ -126,6 +126,15 @@
@failure="handleFailure"
/>
</el-tab-pane>
<el-tab-pane label="统计" name="statistics">
<CleaningStatisticsPanel
:loading="loading.statistics"
:statistics="statistics"
:filters="statisticsFilters"
@refresh="loadStatistics"
@filter="changeStatisticsFilters"
/>
</el-tab-pane>
</el-tabs>
</section>
</main>
@@ -144,6 +153,7 @@ import {
} from '@lucide/vue';
import CleaningTasksPanel from './components/CleaningTasksPanel.vue';
import CleaningSettlementsPanel from './components/CleaningSettlementsPanel.vue';
import CleaningStatisticsPanel from './components/CleaningStatisticsPanel.vue';
import {
ApiError,
assignCleaningTask,
@@ -151,6 +161,7 @@ import {
confirmCleaningSettlement,
executeWechatTransfer,
generateCleaningSettlement,
getCleaningStatistics,
listCleaningSettlements,
listCleaningTasks,
reclaimCleaningTimeouts,
@@ -160,6 +171,7 @@ import {
} from './api';
import type {
CleaningSettlement,
CleaningStatistics,
CleaningTask,
PageResult,
SettlementStatus,
@@ -175,7 +187,7 @@ const lastMessage = ref('');
const taskStatus = ref<TaskStatus | ''>('SUBMITTED');
const settlementStatus = ref<SettlementStatus | ''>('CONFIRMED');
const session = computed(() => ({ token: tokenDraft.value }));
const loading = reactive({ tasks: false, settlements: false });
const loading = reactive({ tasks: false, settlements: false, statistics: false });
const tasks = reactive<PageResult<CleaningTask>>({ items: [], total: 0, page: 1, pageSize: 20 });
const settlements = reactive<PageResult<CleaningSettlement>>({
items: [],
@@ -183,13 +195,24 @@ const settlements = reactive<PageResult<CleaningSettlement>>({
page: 1,
pageSize: 20
});
const metrics = computed(() => ({
submitted: tasks.items.filter((item) => item.status === 'SUBMITTED').length,
completed: tasks.items.filter((item) => item.status === 'COMPLETED').length,
confirmed: settlements.items.filter((item) => item.status === 'CONFIRMED').length,
amountCents: settlements.items.reduce((sum, item) => sum + Number(item.totalRewardCents || 0), 0)
}));
const statisticsFilters = reactive({ from: '', to: '', storeId: '' });
const statistics = reactive<CleaningStatistics>({
summary: {
taskTotal: 0,
pendingReview: 0,
active: 0,
rejected: 0,
completed: 0,
rewardCents: 0,
pendingSettlementCents: 0,
confirmedSettlementCents: 0,
paidSettlementCents: 0
},
byStatus: [],
byStore: [],
settlements: [],
members: []
});
function saveToken() {
localStorage.setItem('qipai.admin.token', tokenDraft.value.trim());
@@ -236,6 +259,23 @@ async function loadSettlements() {
loading.settlements = false;
}
async function loadStatistics() {
loading.statistics = true;
await runAction(async () => {
const result = await getCleaningStatistics(session.value, {
from: statisticsFilters.from || undefined,
to: statisticsFilters.to || undefined,
storeId: statisticsFilters.storeId || undefined
});
Object.assign(statistics.summary, result.summary);
statistics.byStatus = result.byStatus;
statistics.byStore = result.byStore;
statistics.settlements = result.settlements;
statistics.members = result.members;
}, '统计已刷新');
loading.statistics = false;
}
function changeTaskStatus(status: TaskStatus | '') {
taskStatus.value = status;
tasks.page = 1;
@@ -258,31 +298,36 @@ function changeSettlementPage(page: number) {
void loadSettlements();
}
function changeStatisticsFilters(input: { from: string; to: string; storeId: string }) {
Object.assign(statisticsFilters, input);
void loadStatistics();
}
async function handleAssign(input: { taskId: string; cleanerUserId: string; note?: string }) {
await runAction(async () => {
await assignCleaningTask(session.value, input.taskId, input);
await loadTasks();
await Promise.all([loadTasks(), loadStatistics()]);
}, '已指派');
}
async function handleComplete(input: { taskId: string; note?: string }) {
await runAction(async () => {
await completeCleaningTask(session.value, input.taskId, input.note);
await loadTasks();
await Promise.all([loadTasks(), loadStatistics()]);
}, '已验收');
}
async function handleReject(input: { taskId: string; reason: string }) {
await runAction(async () => {
await rejectCleaningTask(session.value, input.taskId, input.reason);
await loadTasks();
await Promise.all([loadTasks(), loadStatistics()]);
}, '已驳回');
}
async function handleReclaim(input: { olderThanMinutes: number; limit: number }) {
await runAction(async () => {
await reclaimCleaningTimeouts(session.value, input);
await loadTasks();
await Promise.all([loadTasks(), loadStatistics()]);
}, '已回收超时任务');
}
@@ -291,14 +336,14 @@ async function handleGenerateSettlement(
) {
await runAction(async () => {
await generateCleaningSettlement(session.value, input);
await Promise.all([loadTasks(), loadSettlements()]);
await Promise.all([loadTasks(), loadSettlements(), loadStatistics()]);
}, '已生成结算单');
}
async function handleConfirmSettlement(input: { settlementId: string; note?: string }) {
await runAction(async () => {
await confirmCleaningSettlement(session.value, input.settlementId, input.note);
await loadSettlements();
await Promise.all([loadSettlements(), loadStatistics()]);
}, '已确认结算单');
}
@@ -308,14 +353,14 @@ async function handleTransfer(input: { settlementId: string; mode: TransferMode;
mode: input.mode,
note: input.note
});
await loadSettlements();
await Promise.all([loadSettlements(), loadStatistics()]);
}, '已提交微信转账');
}
async function handleSyncTransfer(input: { settlementId: string; note?: string }) {
await runAction(async () => {
await syncWechatTransfer(session.value, input.settlementId, input.note);
await loadSettlements();
await Promise.all([loadSettlements(), loadStatistics()]);
}, '已同步微信状态');
}
@@ -329,13 +374,13 @@ async function handleFailure(
payoutReference: input.payoutReference,
note: input.note
});
await loadSettlements();
await Promise.all([loadSettlements(), loadStatistics()]);
}, '已记录失败');
}
onMounted(() => {
if (session.value.token) {
void Promise.all([loadTasks(), loadSettlements()]);
void Promise.all([loadTasks(), loadSettlements(), loadStatistics()]);
}
});
</script>