|
@@ -4,8 +4,13 @@
|
|
|
<el-row :gutter="12" style="padding: 0 12px 12px; row-gap: 12px;">
|
|
|
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
<LayoutHeader title="流失概况">
|
|
|
- <template #aside>
|
|
|
- <div class="data-source-status">数据源状态: Demo数据</div>
|
|
|
+ <template #tooltip-content>
|
|
|
+ 流失概况展示您应用每周的卸载用户量和召回用户量,并展示趋势。
|
|
|
+ <br /><span>卸载设备数:</span>当前时段内,有过卸载行为的设备去重数。
|
|
|
+ 若某设备在当前时段内卸载又重新安装、或多次卸载再安装,都算它为有过卸载行为的1个卸载设备。
|
|
|
+ <br /><span>卸载召回设备:</span>曾经被识别为卸载,当前时段内又被识别为重新安装的设备去重数。
|
|
|
+ 仅统计卸载后90天内重新安装的设备,卸载90天后重新安装的设备不算在卸载召回设备中。
|
|
|
+ 产品页面中,会在每周四刷新上周的卸载召回数据。
|
|
|
</template>
|
|
|
<template #content>
|
|
|
<div class="top-content">
|
|
@@ -42,20 +47,24 @@
|
|
|
</svg>
|
|
|
</div>
|
|
|
|
|
|
- <el-table class="statistics-table" :data="state.dataList" row-key="date" style="width: 100%"
|
|
|
+ <el-table class="statistics-table" :data="paginatedData" row-key="date" style="width: 100%"
|
|
|
:cell-style="cellStyle"
|
|
|
:header-cell-style="headerCellStyle"
|
|
|
v-if="!hideTable"
|
|
|
>
|
|
|
- <el-table-column :label="'日期'" prop="date" show-overflow-tooltip></el-table-column>
|
|
|
- <el-table-column :label="'卸载流失设备'" :formatter="statusFormatter" prop="churn" show-overflow-tooltip></el-table-column>
|
|
|
- <el-table-column :label="'卸载召回设备'" :formatter="statusFormatter" prop="recall" show-overflow-tooltip></el-table-column>
|
|
|
+ <el-table-column :label="'日期'" prop="date" show-overflow-tooltip>
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row.startDate }} - {{ scope.row.endDate }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column :label="'卸载流失设备'" :formatter="statusFormatter" prop="uninstallCounts" show-overflow-tooltip></el-table-column>
|
|
|
+ <el-table-column :label="'卸载召回设备'" :formatter="statusFormatter" prop="recallCounts" show-overflow-tooltip></el-table-column>
|
|
|
</el-table>
|
|
|
|
|
|
<pagination
|
|
|
v-if="!hideTable"
|
|
|
- @current-change="currentChangeHandle"
|
|
|
- @size-change="sizeChangeHandle"
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
v-bind="state.pagination">
|
|
|
</pagination>
|
|
|
</div>
|
|
@@ -71,7 +80,7 @@
|
|
|
import LineChart from '/@/views/count/components/LineChart.vue'
|
|
|
import { BasicTableProps, useTable } from '/@/hooks/table';
|
|
|
import { ref, computed, reactive } from 'vue'
|
|
|
-import { uninstallTrend } from '/@/api/count/churn'
|
|
|
+import { uninstallTrend, uninstallTrendDetail } from '/@/api/count/churn'
|
|
|
import { formatDate } from '/@/utils/formatTime';
|
|
|
import ProgressCard from './ProgressCard.vue'
|
|
|
import LayoutHeader from '/@/views/count/components/LayoutHeader.vue'
|
|
@@ -99,47 +108,14 @@ const trendProgressData = ref({
|
|
|
uninstallRates: 0,
|
|
|
})
|
|
|
|
|
|
-// 流失趋势数据
|
|
|
-const churnTrendData = ref([
|
|
|
- { date: '2025-01-01', value: 25 },
|
|
|
- { date: '2025-01-02', value: 32 },
|
|
|
- { date: '2025-01-03', value: 28 },
|
|
|
- { date: '2025-01-04', value: 45 },
|
|
|
- { date: '2025-01-05', value: 38 },
|
|
|
- { date: '2025-01-06', value: 42 },
|
|
|
- { date: '2025-01-07', value: 35 },
|
|
|
- { date: '2025-01-08', value: 48 },
|
|
|
- { date: '2025-01-09', value: 52 },
|
|
|
- { date: '2025-01-10', value: 39 },
|
|
|
- { date: '2025-01-11', value: 44 },
|
|
|
- { date: '2025-01-12', value: 37 },
|
|
|
- { date: '2025-01-13', value: 41 },
|
|
|
- { date: '2025-01-14', value: 46 }
|
|
|
-])
|
|
|
-
|
|
|
-// 召回趋势数据
|
|
|
-const recallTrendData = ref([
|
|
|
- { date: '2025-01-01', value: 15 },
|
|
|
- { date: '2025-01-02', value: 22 },
|
|
|
- { date: '2025-01-03', value: 18 },
|
|
|
- { date: '2025-01-04', value: 35 },
|
|
|
- { date: '2025-01-05', value: 28 },
|
|
|
- { date: '2025-01-06', value: 32 },
|
|
|
- { date: '2025-01-07', value: 25 },
|
|
|
- { date: '2025-01-08', value: 38 },
|
|
|
- { date: '2025-01-09', value: 42 },
|
|
|
- { date: '2025-01-10', value: 29 },
|
|
|
- { date: '2025-01-11', value: 34 },
|
|
|
- { date: '2025-01-12', value: 27 },
|
|
|
- { date: '2025-01-13', value: 31 },
|
|
|
- { date: '2025-01-14', value: 36 }
|
|
|
-])
|
|
|
-
|
|
|
const activeTab = ref('churnTrend')
|
|
|
|
|
|
// 计算当前图表数据
|
|
|
const currentChartData = computed(() => {
|
|
|
- return activeTab.value === 'churnTrend' ? churnTrendData.value : recallTrendData.value
|
|
|
+ return dataList.value.map((item: any) => ({
|
|
|
+ date: item.endDate,
|
|
|
+ value: activeTab.value === 'churnTrend' ? item.uninstallCounts : item.recallCounts
|
|
|
+ }))
|
|
|
})
|
|
|
|
|
|
// 计算当前图表标题
|
|
@@ -155,51 +131,68 @@ const handleTabClick = (tab: string) => {
|
|
|
const hideTable = ref(false);
|
|
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
|
|
queryForm: {
|
|
|
- ip: '',
|
|
|
},
|
|
|
- pageList: () => Promise.resolve([]),
|
|
|
pagination: {
|
|
|
current: 1,
|
|
|
size: 10,
|
|
|
total: 0,
|
|
|
pageSizes: [5, 10, 20, 50, 100]
|
|
|
},
|
|
|
- dataList: [
|
|
|
- {
|
|
|
- date: '2025-01-01',
|
|
|
- churn: 10,
|
|
|
- recall: 20
|
|
|
- },
|
|
|
- {
|
|
|
- date: '2025-01-02',
|
|
|
- churn: 15,
|
|
|
- recall: 25
|
|
|
- },
|
|
|
- {
|
|
|
- date: '2025-01-03',
|
|
|
- churn: 20,
|
|
|
- recall: 30
|
|
|
- },
|
|
|
- {
|
|
|
- date: '2025-01-04',
|
|
|
- churn: 25,
|
|
|
- recall: 35
|
|
|
- }
|
|
|
- ]
|
|
|
});
|
|
|
-const { getDataList, currentChangeHandle, sizeChangeHandle, tableStyle } = useTable(state);
|
|
|
+
|
|
|
+const dataList = ref([]);
|
|
|
+
|
|
|
const statusFormatter = (row: any, column: any, cellValue: any, index: any) => {
|
|
|
return cellValue || '--';
|
|
|
}
|
|
|
|
|
|
-onMounted(() => {
|
|
|
+const handleCurrentChange = (val: number) => {
|
|
|
+ state.pagination!.current = val;
|
|
|
+};
|
|
|
+
|
|
|
+const handleSizeChange = (val: number) => {
|
|
|
+ state.pagination!.size = val;
|
|
|
+ state.pagination!.current = 1; // 切换每页条数时重置到第一页
|
|
|
+};
|
|
|
+
|
|
|
+// 计算分页后的数据
|
|
|
+const paginatedData = computed(() => {
|
|
|
+ if (!state.pagination || typeof state.pagination.current === 'undefined' || typeof state.pagination.size === 'undefined') {
|
|
|
+ return dataList.value;
|
|
|
+ }
|
|
|
+ const start = (state.pagination.current - 1) * state.pagination.size;
|
|
|
+ const end = start + state.pagination.size;
|
|
|
+ return dataList.value.slice(start, end);
|
|
|
+});
|
|
|
+
|
|
|
+const getUninstallTrend = () => {
|
|
|
uninstallTrend({
|
|
|
startDate: formatDate(new Date(new Date().setDate(new Date().getDate() - 7)), 'YYYY-mm-dd'),
|
|
|
endDate: formatDate(new Date(), 'YYYY-mm-dd'),
|
|
|
timeUnit: 'day',
|
|
|
+ pageNum: state.pagination!.current,
|
|
|
+ pageSize: state.pagination!.size,
|
|
|
}).then(res => {
|
|
|
trendProgressData.value = res.data
|
|
|
})
|
|
|
+}
|
|
|
+
|
|
|
+const getUninstallTrendDetail = () => {
|
|
|
+ uninstallTrendDetail({
|
|
|
+ startDate: formatDate(new Date(new Date().setDate(new Date().getDate() - 50)), 'YYYY-mm-dd'),
|
|
|
+ endDate: formatDate(new Date(), 'YYYY-mm-dd'),
|
|
|
+ timeUnit: 'week',
|
|
|
+ pageNum: state.pagination!.current,
|
|
|
+ pageSize: state.pagination!.size,
|
|
|
+ }).then(res => {
|
|
|
+ dataList.value = res?.data?.records || [];
|
|
|
+ state.pagination!.total = res?.data?.total || 0;
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getUninstallTrend();
|
|
|
+ getUninstallTrendDetail();
|
|
|
})
|
|
|
|
|
|
</script>
|