|
@@ -3,33 +3,10 @@
|
|
|
<div class="!overflow-auto px-1">
|
|
|
<div class="el-card p-2">
|
|
|
<div class="flex justify-between">
|
|
|
- <Title :title="t('activations.analytics')">
|
|
|
- <template #default>
|
|
|
- <el-popover class="box-item" placement="right" trigger="hover" width="250">
|
|
|
- <template #reference>
|
|
|
- <el-icon class="ml-1" style="color: #a4b8cf"><QuestionFilled /></el-icon>
|
|
|
- </template>
|
|
|
- <template #default>
|
|
|
- <div class="ant-popover-inner-content">
|
|
|
- <div class="um-page-tips-content" style="line-height: 24px">
|
|
|
- <p>
|
|
|
- <span class="highlight">启动次数:</span
|
|
|
- ><span
|
|
|
- >打开应用视为启动。完全退出或后台运行超过30s后再次进入应用,视为一次新启动。开发过程中可以通过setSessionContinueMills来自定义两次启动的间隔,默认30s</span
|
|
|
- >
|
|
|
- </p>
|
|
|
- <p><span class="highlight">启动次数占比:</span><span>某日/周/月的启动次数占所选时间段总启动次数的比例</span></p>
|
|
|
- <p>
|
|
|
- <span
|
|
|
- >按天、周或月查看数据可进行版本、渠道的交叉筛选,小时数据最多展示7天并且不支持筛选。周区间定义为周日至次周周六。按周(按月)显示时,界面上用每周的周日(每个月的第一日)来代表该周(该月)</span
|
|
|
- >
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </el-popover>
|
|
|
- </template>
|
|
|
- </Title>
|
|
|
+ <Title :title="t('versionDistribution.analytics')" />
|
|
|
+ <div class="">
|
|
|
+ <el-button type="primary">{{ t('versionDistribution.aijb') }}</el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
<div>
|
|
|
<el-row shadow="hover" class="ml10 mt-2">
|
|
@@ -37,11 +14,6 @@
|
|
|
<el-form-item>
|
|
|
<el-date-picker v-model="formData.time" type="daterange" start-placeholder="开始时间" end-placeholder="结束时间" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-select v-model="selectedChannelCompare" class="w-[140px]" placeholder="全部渠道">
|
|
|
- <el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
<el-form-item>
|
|
|
<el-select v-model="selectedChannelCompare" class="w-[140px]" placeholder="全部版本">
|
|
|
<el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
@@ -52,22 +24,57 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="mt-2 el-card p-2">
|
|
|
+ <Title left-line :title="t('versionDistribution.allVersion')">
|
|
|
+ <template #default>
|
|
|
+ <el-popover class="box-item" placement="right" trigger="hover" width="300">
|
|
|
+ <template #reference>
|
|
|
+ <el-icon class="ml-1" style="color: #a4b8cf"><QuestionFilled /></el-icon>
|
|
|
+ </template>
|
|
|
+ <template #default>
|
|
|
+ <div class="ant-popover-inner-content">
|
|
|
+ <div class="um-page-tips-content" style="line-height: 24px">
|
|
|
+ <p><span>趋势图展示累计用户排名Top10版本的变化趋势</span></p>
|
|
|
+ <p><span class="highlight">新增用户:</span><span>第一次启动应用的用户(以设备为判断标准)</span></p>
|
|
|
+ <p>
|
|
|
+ <span class="highlight">活跃用户:</span
|
|
|
+ ><span>启动过应用的用户(去重),启动过一次的用户即视为活跃用户,包括新用户与老用户</span>
|
|
|
+ </p>
|
|
|
+ <p>
|
|
|
+ <span class="highlight">启动次数:</span
|
|
|
+ ><span
|
|
|
+ >打开应用视为启动。完全退出或后台运行超过30s后再次进入应用,视为一次新启动。开发过程中可以通过setSessionContinueMills来自定义两次启动的间隔,默认30s</span
|
|
|
+ >
|
|
|
+ </p>
|
|
|
+ <p>
|
|
|
+ <span class="highlight">版本累计用户(%):</span
|
|
|
+ ><span>截止到现在,该版本的累计用户(占累计用户全体的比例);若该版本的用户升级到其他版本,则累计用户会减少</span>
|
|
|
+ </p>
|
|
|
+ <p><span class="highlight">升级用户:</span><span>从其他版本升级到该版本的用户(以设备为判断标准)</span></p>
|
|
|
+ <p>
|
|
|
+ <span
|
|
|
+ >如果当日用户先启动老版本然后升级到新版本,分版本查看数据时,此用户在新老版本都会被算为活跃用户(按总体查看数据时不受影响)</span
|
|
|
+ >
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-popover>
|
|
|
+ </template>
|
|
|
+ </Title>
|
|
|
<div class="">
|
|
|
<div class="flex items-center justify-between mb-2 mt-3">
|
|
|
<div>
|
|
|
<el-select v-model="selectedChannelCompare" class="w-[140px] ml-2" style="width: 140px" placeholder="版本对比">
|
|
|
<el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
</el-select>
|
|
|
- <el-button type="primary" class="ml-2">{{ t('activations.version') }}</el-button>
|
|
|
+ <el-button type="primary" class="ml-2">{{ t('versionDistribution.version') }}</el-button>
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-center">
|
|
|
- <el-radio-group v-model="timeGranularity" size="small">
|
|
|
- <el-radio-button label="hour">小时</el-radio-button>
|
|
|
-
|
|
|
- <el-radio-button label="day">天</el-radio-button>
|
|
|
- <el-radio-button label="week">周</el-radio-button>
|
|
|
- <el-radio-button label="month">月</el-radio-button>
|
|
|
+ <el-radio-group v-model="timeGranularity">
|
|
|
+ <el-radio-button label="hour">新增用户</el-radio-button>
|
|
|
+ <el-radio-button label="day">活跃用户</el-radio-button>
|
|
|
+ <el-radio-button label="week">启动次数</el-radio-button>
|
|
|
</el-radio-group>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -80,9 +87,19 @@
|
|
|
<!-- 明细表格 -->
|
|
|
<div class="mt-3">
|
|
|
<div class="flex items-center justify-between mb-2">
|
|
|
- <div class="text-base font-medium cursor-pointer select-none" @click="showDetail1 = !showDetail1">
|
|
|
- {{ showDetail1 ? '收起明细数据' : '展开明细数据' }}
|
|
|
+ <div class="flex">
|
|
|
+ <div class="flex items-center">
|
|
|
+ <el-radio-group v-model="timeGranularity">
|
|
|
+ <el-radio-button label="hour">今日</el-radio-button>
|
|
|
+ <el-radio-button label="day">作日 </el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ <div class="text-base font-medium cursor-pointer select-none ml-3 items-center flex text-[#167AF0]" @click="showDetail1 = !showDetail1">
|
|
|
+ {{ showDetail1 ? '收起明细数据' : '展开明细数据' }}
|
|
|
+ <el-icon class="ml-2"><ArrowDown v-if="showDetail1" /> <ArrowUp v-else /> </el-icon>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+
|
|
|
<div>
|
|
|
<el-button>导出</el-button>
|
|
|
</div>
|
|
@@ -104,6 +121,75 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ <div class="mt-2 el-card p-2">
|
|
|
+ <div class="flex justify-between">
|
|
|
+ <Title left-line :title="'版本用户来源'">
|
|
|
+ <template #default>
|
|
|
+ <el-popover class="box-item" placement="right" trigger="hover" width="300">
|
|
|
+ <template #reference>
|
|
|
+ <el-icon class="ml-1" style="color: #a4b8cf"><QuestionFilled /></el-icon>
|
|
|
+ </template>
|
|
|
+ <template #default>
|
|
|
+ <div class="ant-popover-inner-content">
|
|
|
+ <div class="um-page-tips-content" style="line-height: 24px">
|
|
|
+ <p><span class="highlight">版本用户来源:</span><span>展示各版本用户的主要获取渠道分布</span></p>
|
|
|
+ <p><span class="highlight">新增用户:</span><span>第一次启动应用的用户(以设备为判断标准)</span></p>
|
|
|
+ <p><span class="highlight">升级用户比例:</span><span>从其他版本升级到该版本的用户占比</span></p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-popover>
|
|
|
+ </template>
|
|
|
+ </Title>
|
|
|
+ </div>
|
|
|
+ <div class="flex items-center justify-between mt-3">
|
|
|
+ <el-select v-model="channelDistribution" style="width: 180px" placeholder="新增用户渠道分布">
|
|
|
+ <el-option label="新增用户渠道分布" value="distribution" />
|
|
|
+ </el-select>
|
|
|
+ <div class="ml-4">
|
|
|
+ <el-radio-group v-model="timeRange">
|
|
|
+ <el-radio-button label="yesterday">昨天</el-radio-button>
|
|
|
+ <el-radio-button label="7days">过去7天</el-radio-button>
|
|
|
+ <el-radio-button label="30days">过去30天</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 水平柱状图 -->
|
|
|
+ <div class="mt-4">
|
|
|
+ <div class="relative">
|
|
|
+ <div ref="barChartRef" style="width: 100%; height: 200px"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 明细表格 -->
|
|
|
+ <div class="mt-4">
|
|
|
+ <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="showDetail2 = !showDetail2">
|
|
|
+ {{ showDetail2 ? '收起明细数据' : '展开明细数据' }} <el-icon class="ml-2"><ArrowDown v-if="showDetail2" /> <ArrowUp v-else /> </el-icon>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div>
|
|
|
+ <el-button>导出</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-table v-if="showDetail2" :data="pagedSourceRows" border>
|
|
|
+ <el-table-column prop="channel" label="渠道" min-width="140" />
|
|
|
+ <el-table-column prop="newUsers" label="新增用户" min-width="140" />
|
|
|
+ <el-table-column prop="upgradeRatio" label="升级用户比例" min-width="140" />
|
|
|
+ </el-table>
|
|
|
+ <div v-if="showDetail2" class="flex justify-end mt-3">
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="sourcePage"
|
|
|
+ v-model:page-size="sourcePageSize"
|
|
|
+ background
|
|
|
+ layout="total, prev, pager, next, sizes"
|
|
|
+ :total="sourceRows.length"
|
|
|
+ :page-sizes="[5, 10, 20]"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -112,6 +198,7 @@
|
|
|
import { ref, onMounted, watch, computed, defineAsyncComponent } from 'vue';
|
|
|
import * as echarts from 'echarts';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
+import { QuestionFilled } from '@element-plus/icons-vue';
|
|
|
|
|
|
const { t } = useI18n();
|
|
|
|
|
@@ -128,9 +215,9 @@ const query = () => {
|
|
|
|
|
|
const selectedChannelCompare = ref('');
|
|
|
const channelCompareOptions = [
|
|
|
- { label: '渠道对比', value: 'compare' },
|
|
|
- { label: '渠道A', value: 'a' },
|
|
|
- { label: '渠道B', value: 'b' },
|
|
|
+ { label: '全部版本', value: ' ' },
|
|
|
+ { label: '1.0', value: 'a' },
|
|
|
+ { label: '2.0', value: 'b' },
|
|
|
];
|
|
|
|
|
|
// 图表相关
|
|
@@ -191,6 +278,7 @@ function initLineChart(): void {
|
|
|
|
|
|
onMounted(() => {
|
|
|
initLineChart();
|
|
|
+ initBarChart();
|
|
|
});
|
|
|
|
|
|
watch(timeGranularity, () => {
|
|
@@ -221,17 +309,87 @@ const Title = defineAsyncComponent(() => import('/@/components/Title/index.vue')
|
|
|
// 展开/收起明细
|
|
|
const showDetail1 = ref(true);
|
|
|
|
|
|
+// 版本用户来源相关
|
|
|
+const channelDistribution = ref('distribution');
|
|
|
+const timeRange = ref('yesterday');
|
|
|
+const barChartRef = ref<HTMLDivElement | null>(null);
|
|
|
+let barChartInstance: echarts.ECharts | null = null;
|
|
|
+
|
|
|
+const sourceRows = ref<Array<{ channel: string; newUsers: number; upgradeRatio: string }>>([
|
|
|
+ { channel: '渠道A', newUsers: 100, upgradeRatio: '10%' },
|
|
|
+ { channel: '渠道B', newUsers: 80, upgradeRatio: '15%' },
|
|
|
+ { channel: '渠道C', newUsers: 70, upgradeRatio: '20%' },
|
|
|
+ { channel: '渠道D', newUsers: 60, upgradeRatio: '25%' },
|
|
|
+ { channel: '渠道E', newUsers: 50, upgradeRatio: '30%' },
|
|
|
+ { channel: '渠道F', newUsers: 40, upgradeRatio: '35%' },
|
|
|
+ { channel: '渠道G', newUsers: 30, upgradeRatio: '40%' },
|
|
|
+ { channel: '渠道H', newUsers: 20, upgradeRatio: '45%' },
|
|
|
+ { channel: '渠道I', newUsers: 10, upgradeRatio: '50%' },
|
|
|
+ { channel: '渠道J', newUsers: 5, upgradeRatio: '55%' },
|
|
|
+]);
|
|
|
+
|
|
|
+function initBarChart(): void {
|
|
|
+ if (!barChartRef.value) return;
|
|
|
+ if (barChartInstance) barChartInstance.dispose();
|
|
|
+ barChartInstance = echarts.init(barChartRef.value);
|
|
|
+ const option: echarts.EChartsOption = {
|
|
|
+ tooltip: { trigger: 'axis' },
|
|
|
+ grid: { left: 40, right: 20, top: 20, bottom: 30 },
|
|
|
+ xAxis: {
|
|
|
+ type: 'value',
|
|
|
+ axisLine: { show: false },
|
|
|
+ splitLine: { lineStyle: { color: '#f3f4f6' } },
|
|
|
+ axisLabel: { color: '#6b7280' },
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: sourceRows.value.map((d) => d.channel),
|
|
|
+ axisLine: { lineStyle: { color: '#e5e7eb' } },
|
|
|
+ axisLabel: { color: '#6b7280' },
|
|
|
+ axisTick: { alignWithLabel: true },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '新增用户',
|
|
|
+ type: 'bar',
|
|
|
+ barWidth: '60%',
|
|
|
+ itemStyle: { color: '#409EFF' },
|
|
|
+ data: sourceRows.value.map((d) => d.newUsers),
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ barChartInstance.setOption(option);
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ initBarChart();
|
|
|
+});
|
|
|
+
|
|
|
+watch(timeRange, () => {
|
|
|
+ // 静态页面:仅重新渲染
|
|
|
+ initBarChart();
|
|
|
+});
|
|
|
+
|
|
|
+const sourcePage = ref(1);
|
|
|
+const sourcePageSize = ref(5);
|
|
|
+const pagedSourceRows = computed(() => {
|
|
|
+ const startIndex = (sourcePage.value - 1) * sourcePageSize.value;
|
|
|
+
|
|
|
+ return sourceRows.value.slice(startIndex, startIndex + sourcePageSize.value);
|
|
|
+});
|
|
|
|
|
|
+const showDetail2 = ref(true);
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.highlight {
|
|
|
color: #2196f3;
|
|
|
}
|
|
|
-.el-form-item--default{
|
|
|
+.el-form-item--default {
|
|
|
margin-bottom: 0;
|
|
|
}
|
|
|
-.el-form.el-form--inline .el-form-item--default.el-form-item:last-of-type, .el-form.el-form--inline .el-form-item--small.el-form-item:last-of-type{
|
|
|
+.el-form.el-form--inline .el-form-item--default.el-form-item:last-of-type,
|
|
|
+.el-form.el-form--inline .el-form-item--small.el-form-item:last-of-type {
|
|
|
margin-bottom: 0 !important;
|
|
|
}
|
|
|
</style>
|