<script lang="ts" setup>
|
import { computed, ref, watch } from 'vue';
|
|
import { Page } from '@vben/common-ui';
|
|
import { Card, Col, DatePicker, Input, Row, Table } from 'ant-design-vue';
|
import dayjs from 'dayjs';
|
|
import { getRangePickerDefaultProps } from '#/utils';
|
import { getTraceLocationDistribution, getTraceLocationVisits } from '#/api/mes/trace-consumer-scan';
|
import type { MesTraceConsumerScanApi } from '#/api/mes/trace-consumer-scan';
|
|
import DistributionChart from './modules/distribution-chart.vue';
|
import SummaryCards from './modules/summary-cards.vue';
|
import TrendChart from './modules/trend-chart.vue';
|
|
defineOptions({ name: 'MesConsumerScanStatistics' });
|
|
/** 统计时间范围,默认近 30 天(valueFormat 已转为 yyyy-MM-dd HH:mm:ss) */
|
const dateRange = ref<[string, string] | undefined>([
|
dayjs().subtract(30, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
]);
|
|
const startTime = computed(() => dateRange.value?.[0]);
|
const endTime = computed(() => dateRange.value?.[1]);
|
|
const traceCode = ref('');
|
const pageNo = ref(1);
|
const locationLoading = ref(false);
|
const locationRows = ref<MesTraceConsumerScanApi.LocationVisit[]>([]);
|
const locationDistribution = ref<MesTraceConsumerScanApi.LocationDistribution[]>([]);
|
const locationTotal = ref(0);
|
const locationColumns = [
|
{ title: '访问时间', dataIndex: 'visitTime', key: 'visitTime' },
|
{ title: '追溯码', dataIndex: 'traceCode', key: 'traceCode' },
|
{ title: '纬度', dataIndex: 'latitude', key: 'latitude' },
|
{ title: '经度', dataIndex: 'longitude', key: 'longitude' },
|
{ title: '精度(米)', dataIndex: 'accuracy', key: 'accuracy' },
|
{ title: '位置', key: 'map' },
|
];
|
|
async function loadLocationVisits() {
|
locationLoading.value = true;
|
try {
|
const page = await getTraceLocationVisits({
|
pageNo: pageNo.value,
|
pageSize: 20,
|
startTime: startTime.value,
|
endTime: endTime.value,
|
traceCode: traceCode.value.trim() || undefined,
|
});
|
locationRows.value = page.list;
|
locationTotal.value = page.total;
|
locationDistribution.value = await getTraceLocationDistribution({
|
startTime: startTime.value,
|
endTime: endTime.value,
|
traceCode: traceCode.value.trim() || undefined,
|
});
|
} finally {
|
locationLoading.value = false;
|
}
|
}
|
|
watch([startTime, endTime, traceCode], () => {
|
pageNo.value = 1;
|
loadLocationVisits();
|
}, { immediate: true });
|
|
function mapUrl(row: Record<string, any>) {
|
const lat = Number(row.latitude);
|
const lon = Number(row.longitude);
|
return `https://www.openstreetmap.org/?mlat=${lat}&mlon=${lon}#map=16/${lat}/${lon}`;
|
}
|
|
function handleLocationPageChange(pagination: { current?: number }) {
|
pageNo.value = pagination.current || 1;
|
loadLocationVisits();
|
}
|
</script>
|
|
<template>
|
<Page>
|
<Card title="消费者扫码统计" class="mb-4">
|
<div class="flex flex-wrap items-center gap-2">
|
<span class="text-muted-foreground text-sm">统计时间范围</span>
|
<DatePicker.RangePicker
|
v-model:value="dateRange"
|
v-bind="getRangePickerDefaultProps()"
|
/>
|
</div>
|
</Card>
|
|
<SummaryCards :start-time="startTime" :end-time="endTime" />
|
|
<Row :gutter="16">
|
<Col :lg="14" :md="24" :sm="24" :xl="14" :xs="24" class="mb-4">
|
<TrendChart :start-time="startTime" :end-time="endTime" />
|
</Col>
|
<Col :lg="10" :md="24" :sm="24" :xl="10" :xs="24" class="mb-4">
|
<DistributionChart :start-time="startTime" :end-time="endTime" />
|
</Col>
|
</Row>
|
|
<Card title="定位授权访问" class="mb-4">
|
<p class="mb-3 text-sm text-muted-foreground">
|
当前筛选条件下共 {{ locationTotal }} 次授权定位访问;下表显示用户授权后上传的位置及定位精度。
|
</p>
|
<Input v-model:value="traceCode" class="mb-3 max-w-xs" allow-clear placeholder="按追溯码筛选" />
|
<div v-if="locationDistribution.length" class="mb-4">
|
<div class="mb-2 font-medium">热门位置(坐标保留两位小数,最多 20 处)</div>
|
<div class="flex flex-wrap gap-2">
|
<span v-for="item in locationDistribution" :key="`${item.latitude},${item.longitude}`" class="rounded bg-green-50 px-3 py-1 text-sm text-green-800">
|
{{ item.latitude }}, {{ item.longitude }}:{{ item.count }} 次
|
</span>
|
</div>
|
</div>
|
<Table
|
:columns="locationColumns"
|
:data-source="locationRows"
|
:loading="locationLoading"
|
:pagination="{ current: pageNo, pageSize: 20, total: locationTotal, showSizeChanger: false }"
|
:row-key="(row: MesTraceConsumerScanApi.LocationVisit) => row.id"
|
:scroll="{ x: 850 }"
|
@change="handleLocationPageChange"
|
>
|
<template #bodyCell="{ column, record }">
|
<a v-if="column.key === 'map'" :href="mapUrl(record)" target="_blank" rel="noopener noreferrer">查看地图</a>
|
</template>
|
</Table>
|
</Card>
|
</Page>
|
</template>
|