|
@@ -4,15 +4,22 @@
|
|
|
<div class="">
|
|
|
<div class="flex items-center justify-between mb-2 mt-3">
|
|
|
<div class="flex items-center">
|
|
|
- <el-select v-model="industryCompare" class="!w-[120px]" clearable @change="handleCompareChange">
|
|
|
+ <el-select v-model="chartType" class="!w-[140px]" @change="onChangeType">
|
|
|
+ <el-option label="活跃趋势" value="活跃趋势" />
|
|
|
+ <el-option label="活跃构成" value="活跃构成" />
|
|
|
+ <el-option label="活跃粘度" value="活跃粘度" />
|
|
|
+ <el-option label="分时活跃用户" value="分时活跃用户" />
|
|
|
+ <el-option label="周活跃度" value="周活跃度" />
|
|
|
+ <el-option label="月活跃度" value="月活跃度" />
|
|
|
+ </el-select>
|
|
|
+ <el-select v-model="industryCompare" class="!w-[120px] ml-2" clearable @change="handleCompareChange">
|
|
|
<el-option label="版本对比" value="version" />
|
|
|
<el-option label="渠道对比" value="channel" />
|
|
|
<el-option label="时段对比" value="time" />
|
|
|
</el-select>
|
|
|
|
|
|
<!-- 版本对比和渠道对比使用popover -->
|
|
|
- <el-popover v-if="industryCompare !== 'time' && !industryCompare" placement="bottom" trigger="click"
|
|
|
- width="400">
|
|
|
+ <el-popover v-if="industryCompare !== 'time' && !industryCompare" placement="bottom" trigger="click" width="400">
|
|
|
<template #reference>
|
|
|
<el-button class="ml-2">{{ t('addUser.average') }}</el-button>
|
|
|
</template>
|
|
@@ -20,12 +27,16 @@
|
|
|
<div class="p-3">
|
|
|
<div class="mb-3">
|
|
|
<label class="text-sm font-medium mb-2 block">{{ getCompareTitle() }}</label>
|
|
|
- <el-input v-model="searchKeyword" :placeholder="`请搜索${getCompareTypeText()}`"
|
|
|
- clearable @input="filterCompareOptions" size="small" />
|
|
|
+ <el-input
|
|
|
+ v-model="searchKeyword"
|
|
|
+ :placeholder="`请搜索${getCompareTypeText()}`"
|
|
|
+ clearable
|
|
|
+ @input="filterCompareOptions"
|
|
|
+ size="small"
|
|
|
+ />
|
|
|
</div>
|
|
|
<div class="max-h-60 overflow-y-auto">
|
|
|
- <el-checkbox-group v-model="selectedCompareItems"
|
|
|
- @change="handleCompareItemsChange">
|
|
|
+ <el-checkbox-group v-model="selectedCompareItems" @change="handleCompareItemsChange">
|
|
|
<div v-for="item in filteredCompareOptions" :key="item" class="mb-2">
|
|
|
<el-checkbox :label="item" size="small">{{ item }}</el-checkbox>
|
|
|
</div>
|
|
@@ -33,22 +44,35 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
- </el-popover>
|
|
|
+ </el-popover>
|
|
|
|
|
|
<!-- 时段对比使用日期选择 -->
|
|
|
- <el-popover v-if="industryCompare === 'time'" placement="bottom" trigger="click" width="300"
|
|
|
- :visible="timeCompareVisible" :hide-after="0" :persistent="true">
|
|
|
+ <el-popover
|
|
|
+ v-if="industryCompare === 'time'"
|
|
|
+ placement="bottom"
|
|
|
+ trigger="click"
|
|
|
+ width="300"
|
|
|
+ :visible="timeCompareVisible"
|
|
|
+ :hide-after="0"
|
|
|
+ :persistent="true"
|
|
|
+ >
|
|
|
<template #reference>
|
|
|
- <el-button class="ml-2"
|
|
|
- @click="timeCompareVisible = !timeCompareVisible">{{ t('addUser.average') }}</el-button>
|
|
|
+ <el-button class="ml-2" @click="timeCompareVisible = !timeCompareVisible">{{ t('addUser.average') }}</el-button>
|
|
|
</template>
|
|
|
<template #default>
|
|
|
<div class="p-3">
|
|
|
<div class="mb-3">
|
|
|
<label class="text-sm font-medium mb-2 block">选择对比时段</label>
|
|
|
- <el-date-picker v-model="timeCompareRange" type="date" format="YYYY-MM-DD"
|
|
|
- value-format="YYYY-MM-DD" :disabled-date="disableAfterToday"
|
|
|
- @change="handleTimeCompareChange" style="width: 100%" :clearable="false" />
|
|
|
+ <el-date-picker
|
|
|
+ v-model="timeCompareRange"
|
|
|
+ type="date"
|
|
|
+ format="YYYY-MM-DD"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ :disabled-date="disableAfterToday"
|
|
|
+ @change="handleTimeCompareChange"
|
|
|
+ style="width: 100%"
|
|
|
+ :clearable="false"
|
|
|
+ />
|
|
|
</div>
|
|
|
</div>
|
|
|
<!-- <div class="flex justify-end">
|
|
@@ -61,7 +85,7 @@
|
|
|
<el-button link class="ml-2" @click="clearCompare(), getData('clearAll')">清除</el-button>
|
|
|
</div>
|
|
|
<div class="flex items-center">
|
|
|
- <el-radio-group v-model="formData.timeUnit" @change="getData">
|
|
|
+ <el-radio-group v-model="formData.timeUnit" @change="getData(''), getBottomDetail()">
|
|
|
<el-radio-button label="day" :disabled="isDayDisabled">天</el-radio-button>
|
|
|
<el-radio-button label="week" :disabled="isWeekDisabled">周</el-radio-button>
|
|
|
<el-radio-button label="month" :disabled="isMonthDisabled">月</el-radio-button>
|
|
@@ -77,9 +101,9 @@
|
|
|
<!-- 明细表格 -->
|
|
|
<div class="mt-3">
|
|
|
<div class="flex items-center justify-between mb-2">
|
|
|
- <div class="text-base font-medium cursor-pointer select-none ml-3 items-center flex text-[#167AF0]"
|
|
|
- @click="showDetail = !showDetail">
|
|
|
- {{ showDetail ? '收起明细数据' : '展开明细数据' }} <el-icon class="ml-2">
|
|
|
+ <div class="text-base font-medium cursor-pointer select-none ml-3 items-center flex text-[#167AF0]" @click="showDetail = !showDetail">
|
|
|
+ {{ showDetail ? '收起明细数据' : '展开明细数据' }}
|
|
|
+ <el-icon class="ml-2">
|
|
|
<ArrowDown v-if="showDetail" />
|
|
|
<ArrowUp v-else />
|
|
|
</el-icon>
|
|
@@ -88,26 +112,38 @@
|
|
|
<el-button type="primary" text>导出</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <el-table v-if="showDetail" :data="tableData" border>
|
|
|
- <el-table-column prop="date" label="日期" min-width="140" />
|
|
|
- <el-table-column prop="date" label="活跃用户数" min-width="140" />
|
|
|
- <el-table-column prop="date" label="活跃构成(新用户占比)" min-width="140" />
|
|
|
- <el-table-column prop="date" label="DAU/过去7日活跃用户" min-width="140" />
|
|
|
- <el-table-column prop="date" label="DAU/过去30日活跃用户" min-width="140" />
|
|
|
- <el-table-column label="新增用户(占比)" align="right" min-width="220">
|
|
|
- <template #default="scope">
|
|
|
- <div class="flex items-center justify-end w-full">
|
|
|
- <span>{{ scope.row.newUser }}</span>
|
|
|
- <span class="text-gray-500 text-xs ml-2">({{ (scope.row.newUserRate * 100).toFixed(2)
|
|
|
- }}%)</span>
|
|
|
- </div>
|
|
|
+ <el-table v-if="showDetail" :data="tableData" border v-loading.fullscreen.lock="loading">
|
|
|
+ <el-table-column prop="date" label="日期" min-width="100" />
|
|
|
+ <el-table-column prop="activeUser" label="活跃用户数" align="right" min-width="100" />
|
|
|
+ <el-table-column label="活跃构成(新用户占比)" align="right" min-width="140">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <div v-if="row.newUserRate" class="flex items-center justify-end w-full">{{ (row.newUserRate * 100).toFixed(2) }}%</div>
|
|
|
+ <span v-else>--</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="DAU/过去7日活跃用户" align="right" min-width="140">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <div v-if="row.wauRate" class="flex items-center justify-end w-full">{{ (row.wauRate * 100).toFixed(2) }}%</div>
|
|
|
+ <span v-else>--</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="DAU/过去30日活跃用户" align="right" min-width="140">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <div v-if="row.mauRate" class="flex items-center justify-end w-full">{{ (row.mauRate * 100).toFixed(2) }}%</div>
|
|
|
+ <span v-else>--</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
<div v-if="showDetail" class="flex justify-end mt-3">
|
|
|
- <el-pagination v-model:current-page="pagination.current" v-model:page-size="pagination.size"
|
|
|
- @change="getBottomDetail" background layout="total, prev, pager, next, sizes"
|
|
|
- :total="pagination.total" :page-sizes="[5, 10, 20]" />
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="pagination.current"
|
|
|
+ v-model:page-size="pagination.size"
|
|
|
+ @change="getBottomDetail"
|
|
|
+ background
|
|
|
+ layout="total, prev, pager, next, sizes"
|
|
|
+ :total="pagination.total"
|
|
|
+ :page-sizes="[5, 10, 20]"
|
|
|
+ />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -120,18 +156,18 @@ import { useI18n } from 'vue-i18n';
|
|
|
import dayjs from 'dayjs';
|
|
|
import { getDaysBetweenDates } from '/@/utils/formatTime';
|
|
|
import { getAppVersion, getAppChannel } from '/@/api/common/common';
|
|
|
-import { getTrend, getTrendDetail } from '/@/api/count/addUser';
|
|
|
+import { getTrend, getTrendDetail, getCompose, getViscosity, getWeekrate, getMonthrate } from '/@/api/count/activeUser';
|
|
|
|
|
|
const { t } = useI18n();
|
|
|
-const Title = defineAsyncComponent(() => import('/@/components/Title/index.vue'));
|
|
|
|
|
|
-const industryCompare = ref('');
|
|
|
+const industryCompare = ref('');
|
|
|
const selectedCompareItems = ref<string[]>([]);
|
|
|
const searchKeyword = ref('');
|
|
|
const compareOptions = ref<string[]>([]);
|
|
|
const filteredCompareOptions = ref<string[]>([]);
|
|
|
const timeCompareRange = ref<string>('');
|
|
|
const timeCompareVisible = ref(false);
|
|
|
+const chartType = ref('活跃趋势');
|
|
|
|
|
|
const masterData = ref(<any>{
|
|
|
//图表主数据
|
|
@@ -176,8 +212,21 @@ const pagination = ref({
|
|
|
});
|
|
|
const previousCompareItems = ref<string[]>([]);
|
|
|
const chartTimes = ref<string[]>([]);
|
|
|
+const loading = ref(false);
|
|
|
+
|
|
|
+const onChangeType = (value: string) => {
|
|
|
+ if (value !== '分时活跃用户') {
|
|
|
+ getData('clearAll');
|
|
|
+ emit('query', '');
|
|
|
+
|
|
|
+ }
|
|
|
+ if(value === '分时活跃用户' || value === '周活跃度' || value === '月活跃率'){
|
|
|
+ emit('query', 'disabled');
|
|
|
+ }
|
|
|
+};
|
|
|
|
|
|
const getData = async (type: string) => {
|
|
|
+ loading.value = true;
|
|
|
//上方图表
|
|
|
if (type === 'clearAll') {
|
|
|
lineChartData.value = { dates: [], items: [] };
|
|
@@ -190,9 +239,32 @@ const getData = async (type: string) => {
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
- const res = await getTrend({ ...formData.value });
|
|
|
- const data = res?.data || [];
|
|
|
+ let res;
|
|
|
+ let data;
|
|
|
+ if (chartType.value == '活跃趋势') {
|
|
|
+ res = await getTrend(formData.value);
|
|
|
+ data = res.data || [];
|
|
|
+ } else if (chartType.value == '活跃构成') {
|
|
|
+ res = await getCompose(formData.value);
|
|
|
+ data = res.data || [];
|
|
|
+ } else if (chartType.value == '活跃粘度') {
|
|
|
+ res = await getViscosity(formData.value);
|
|
|
+ data = res.data || [];
|
|
|
+ } else if (chartType.value == '周活跃度') {
|
|
|
+ res = await getWeekrate(formData.value);
|
|
|
+ data = res.data || [];
|
|
|
+ } else if (chartType.value == '月活跃度') {
|
|
|
+ res = await getMonthrate(formData.value);
|
|
|
+ data = res.data || [];
|
|
|
+ }
|
|
|
|
|
|
+ // const res = await getTrend({ ...formData.value });
|
|
|
+ // const data = res?.data || [];
|
|
|
+ initChartData(data);
|
|
|
+ loading.value = false;
|
|
|
+};
|
|
|
+
|
|
|
+const initChartData = async (data: any) => {
|
|
|
if (!industryCompare.value) {
|
|
|
masterData.value = data;
|
|
|
lineChartData.value.items = data.items.map((item: any, index: number) => {
|
|
@@ -241,6 +313,7 @@ const getData = async (type: string) => {
|
|
|
lineChartData.value.dates = masterData.value.dates;
|
|
|
initLineChart();
|
|
|
};
|
|
|
+
|
|
|
const getBottomDetail = async () => {
|
|
|
// 下方明细表
|
|
|
const resDetail = await getTrendDetail({ ...formData.value, ...pagination.value });
|
|
@@ -432,7 +505,9 @@ async function initCompareOptions() {
|
|
|
function handleTimeCompareChange(value: string) {
|
|
|
timeCompareRange.value = value;
|
|
|
formData.value.toDate = timeCompareRange.value;
|
|
|
- formData.value.fromDate = dayjs(timeCompareRange.value).subtract(getDaysBetweenDates(props.formData?.time[0], props.formData?.time[1]), 'day').format('YYYY-MM-DD');
|
|
|
+ formData.value.fromDate = dayjs(timeCompareRange.value)
|
|
|
+ .subtract(getDaysBetweenDates(props.formData?.time[0], props.formData?.time[1]), 'day')
|
|
|
+ .format('YYYY-MM-DD');
|
|
|
formData.value.channel = props.formData?.channel ? [props.formData?.channel] : [];
|
|
|
formData.value.version = props.formData?.version ? [props.formData?.version] : [];
|
|
|
|