|
@@ -92,15 +92,9 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
</Lcard>
|
|
|
- <Lcard :height="975">
|
|
|
- <el-date-picker
|
|
|
- style="float: left;"
|
|
|
- v-model="timeRange"
|
|
|
- type="datetimerange"
|
|
|
- range-separator="To"
|
|
|
- start-placeholder="Start date"
|
|
|
- end-placeholder="End date"
|
|
|
- />
|
|
|
+ <Lcard :height="975">
|
|
|
+ <el-date-picker style="float: left;" v-model="timeRange" type="datetimerange" range-separator="To"
|
|
|
+ start-placeholder="Start date" end-placeholder="End date" />
|
|
|
<div class="link" style="float: right;">订阅</div>
|
|
|
<div class="link" style="float: right;">编辑指标</div>
|
|
|
<div class="link" style="float: right;">生成AI简报</div>
|
|
@@ -115,27 +109,81 @@
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<div class="flex items-center">
|
|
|
- <el-radio-group size="small">
|
|
|
+ <el-radio-group v-model="lineChartUser">
|
|
|
<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>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
<div class="relative">
|
|
|
<div ref="qChartRef" style="width: 100%; height: 320px"></div>
|
|
|
</div>
|
|
|
+ <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>
|
|
|
+ <div>
|
|
|
+ <el-button>导出</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-table v-if="showDetail1" :data="pagedTableRows" border>
|
|
|
+ <el-table-column prop="date" label="日期" min-width="140" />
|
|
|
+ <el-table-column label="新增用户(占比)" min-width="220">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="flex items-center justify-between w-full">
|
|
|
+ <span>{{ scope.row.newUsers }}</span>
|
|
|
+ <span class="text-gray-500 text-xs">{{ scope.row.ratio }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <div v-if="showDetail1" class="flex justify-end mt-3">
|
|
|
+ <el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" background
|
|
|
+ layout="total, prev, pager, next, sizes" :total="tableRows.length" :page-sizes="[5, 10, 20]" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</Lcard>
|
|
|
- <Lcard :height="440" />
|
|
|
- <Lcard :height="440" />
|
|
|
- <Lcard :height="440" />
|
|
|
- </div>
|
|
|
+ <Lcard :height="660">
|
|
|
+ <div class="msg">
|
|
|
+ <el-icon class="ml-1" style="color: #a4b8cf; vertical-align: middle;">
|
|
|
+ <InfoFilled />
|
|
|
+ </el-icon>
|
|
|
+ 点击下列环形图中单个版本/渠道将跳转到对应的详情页
|
|
|
+ </div>
|
|
|
+ <div class="box1">
|
|
|
+ <lcard :height="540" style="margin-right: 20px;">
|
|
|
+ <div class="box1-title" style="float: left;">TOP版本</div>
|
|
|
+ <div class="link" style="float: right;">详情</div>
|
|
|
+ <div class="line" style="margin-top: 40px;"></div>
|
|
|
+ <el-radio-group v-model="circleEchartUser1">
|
|
|
+ <el-radio-button label="hour">新增用户</el-radio-button>
|
|
|
+ <el-radio-button label="day">活跃用户</el-radio-button>
|
|
|
+ <el-radio-button label="month">累计用户</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ <div ref="circleEchartRef1" style="width: 100%; height: 380px;"></div>
|
|
|
+ </lcard>
|
|
|
+ <lcard :height="540">
|
|
|
+ <div class="box1-title" style="float: left;">TOP版本</div>
|
|
|
+ <div class="link" style="float: right;">详情</div>
|
|
|
+ <div class="line" style="margin-top: 40px;"></div>
|
|
|
+ <el-radio-group v-model="circleEchartUser1">
|
|
|
+ <el-radio-button label="hour">新增用户</el-radio-button>
|
|
|
+ <el-radio-button label="day">活跃用户</el-radio-button>
|
|
|
+ <el-radio-button label="month">累计用户</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ <div ref="circleEchartRef2" style="width: 100%; height: 380px;"></div>
|
|
|
+ </lcard>
|
|
|
+ </div>
|
|
|
+ </Lcard>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" name="countMainTrend" setup>
|
|
|
-import { ref, onMounted, watch, computed, defineAsyncComponent } from 'vue';
|
|
|
+// import { ref, onMounted, watch, computed, defineAsyncComponent } from 'vue';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
import * as echarts from 'echarts';
|
|
|
// 引入组件
|
|
@@ -146,56 +194,196 @@ const { t } = useI18n();
|
|
|
const timeRange = ref(null);
|
|
|
let qualityChart: echarts.ECharts | null = null;
|
|
|
const qChartRef = ref(null);
|
|
|
+const lineChartUser = ref(null);
|
|
|
onMounted(() => {
|
|
|
- // initLineChart();
|
|
|
- initQualityChart();
|
|
|
+ setTimeout(() => {
|
|
|
+ initQualityChart();
|
|
|
+ initCircleChart1();
|
|
|
+ initCircleChart2();
|
|
|
+ }, 500)
|
|
|
console.log(qualityChart);
|
|
|
- // initBehaviorChart();
|
|
|
});
|
|
|
const qualityXAxis = ref<string[]>([
|
|
|
- '2025-07-01',
|
|
|
- '2025-07-08',
|
|
|
- '2025-07-15',
|
|
|
- '2025-07-22',
|
|
|
- '2025-07-29',
|
|
|
- '2025-08-05',
|
|
|
- '2025-08-12',
|
|
|
- '2025-08-19',
|
|
|
- '2025-08-26',
|
|
|
- '2025-09-02',
|
|
|
- '2025-09-09',
|
|
|
- '2025-09-16',
|
|
|
+ '2025-07-01',
|
|
|
+ '2025-07-08',
|
|
|
+ '2025-07-15',
|
|
|
+ '2025-07-22',
|
|
|
+ '2025-07-29',
|
|
|
+ '2025-08-05',
|
|
|
+ '2025-08-12',
|
|
|
+ '2025-08-19',
|
|
|
+ '2025-08-26',
|
|
|
+ '2025-09-02',
|
|
|
+ '2025-09-09',
|
|
|
+ '2025-09-16',
|
|
|
]);
|
|
|
const retentionSeries = ref<number[]>([20, 23, 27, 24, 22, 15, 5, 4, 16, 26, 25, 2]);
|
|
|
const industryAvgSeries = ref<number[]>([16, 18, 20, 24, 25, 24, 16, 10, 15, 22, 21, 12]);
|
|
|
const peerSameScaleSeries = ref<number[]>([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
|
|
function initQualityChart(): void {
|
|
|
- console.log(qChartRef.value);
|
|
|
- if (!qChartRef.value) return;
|
|
|
- if (qualityChart) qualityChart.dispose();
|
|
|
- qualityChart = echarts.init( qChartRef.value);
|
|
|
- debugger
|
|
|
- const option: echarts.EChartsOption = {
|
|
|
- tooltip: { trigger: 'axis', valueFormatter: (v) => `${v}%` },
|
|
|
- legend: { data: ['留存率', '同行业App', '同行业同规模App'] },
|
|
|
- grid: { left: 40, right: 20, top: 30, bottom: 30 },
|
|
|
- xAxis: { type: 'category', data: qualityXAxis.value },
|
|
|
- yAxis: {
|
|
|
- type: 'value',
|
|
|
- min: 0,
|
|
|
- max: 30,
|
|
|
- axisLabel: { formatter: '{value}%' },
|
|
|
- splitLine: { lineStyle: { color: '#f3f4f6' } },
|
|
|
- },
|
|
|
- series: [
|
|
|
- { name: '留存率', type: 'line', smooth: true, data: retentionSeries.value },
|
|
|
- { name: '同行业App', type: 'line', smooth: true, data: industryAvgSeries.value, color: '#f59e0b' },
|
|
|
- { name: '同行业同规模App', type: 'line', smooth: true, data: peerSameScaleSeries.value, color: '#60a5fa' },
|
|
|
- ],
|
|
|
- };
|
|
|
- qualityChart.setOption(option);
|
|
|
+ console.log(qChartRef.value, qChartRef);
|
|
|
+ if (!qChartRef.value) return;
|
|
|
+ if (qualityChart) qualityChart.dispose();
|
|
|
+ qualityChart = echarts.init(qChartRef.value);
|
|
|
+ const option: echarts.EChartsOption = {
|
|
|
+ tooltip: { trigger: 'axis', valueFormatter: (v) => `${v}%` },
|
|
|
+ legend: { data: ['留存率', '同行业App', '同行业同规模App'] },
|
|
|
+ grid: { left: 40, right: 20, top: 30, bottom: 30 },
|
|
|
+ xAxis: { type: 'category', data: qualityXAxis.value },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ min: 0,
|
|
|
+ max: 30,
|
|
|
+ axisLabel: { formatter: '{value}%' },
|
|
|
+ splitLine: { lineStyle: { color: '#f3f4f6' } },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ { name: '留存率', type: 'line', smooth: true, data: retentionSeries.value },
|
|
|
+ { name: '同行业App', type: 'line', smooth: true, data: industryAvgSeries.value, color: '#f59e0b' },
|
|
|
+ { name: '同行业同规模App', type: 'line', smooth: true, data: peerSameScaleSeries.value, color: '#60a5fa' },
|
|
|
+ ],
|
|
|
+ };
|
|
|
+ qualityChart.setOption(option);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+interface TableRow {
|
|
|
+ date: string;
|
|
|
+ newUsers: number;
|
|
|
+ ratio: string;
|
|
|
}
|
|
|
+// 展开/收起明细
|
|
|
+const showDetail1 = ref(true);
|
|
|
+const pagedTableRows = computed(() => {
|
|
|
+ const startIndex = (currentPage.value - 1) * pageSize.value;
|
|
|
+ return tableRows.value.slice(startIndex, startIndex + pageSize.value);
|
|
|
+});
|
|
|
+// 表格相关(静态数据)
|
|
|
+const currentPage = ref(1);
|
|
|
+const pageSize = ref(5);
|
|
|
+const tableRows = ref<TableRow[]>(
|
|
|
+ Array.from({ length: 42 }).map((_, idx) => ({
|
|
|
+ date: `2025-08-${String(11).padStart(2, '0')}`,
|
|
|
+ newUsers: 727,
|
|
|
+ ratio: '97.45%',
|
|
|
+ }))
|
|
|
+);
|
|
|
+
|
|
|
|
|
|
+// 环形图
|
|
|
+// 添加圆环图相关的引用和数据
|
|
|
+const circleEchartRef1 = ref(null);
|
|
|
+let circleChart1: echarts.ECharts | null = null;
|
|
|
+const circleEchartUser1 = ref('hour');
|
|
|
+
|
|
|
+const circleEchartRef2 = ref(null);
|
|
|
+let circleChart2: echarts.ECharts | null = null;
|
|
|
+const circleEchartUser2 = ref('hour');
|
|
|
+
|
|
|
+// 初始化圆环图
|
|
|
+function initCircleChart1(): void {
|
|
|
+ if (!circleEchartRef1.value) return;
|
|
|
+ if (circleChart1) circleChart1.dispose();
|
|
|
+
|
|
|
+ circleChart1 = echarts.init(circleEchartRef1.value);
|
|
|
+
|
|
|
+ const option: echarts.EChartsOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ bottom: '0',
|
|
|
+ left: 'center'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '版本分布',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['50%', '70%'], // 设置内半径和外半径,形成圆环效果
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ padAngle: 2, // 扇区间隙
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 5 // 扇区圆角
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ formatter: '{b}: {d}%' // 显示标签和百分比
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ fontSize: 14,
|
|
|
+ fontWeight: 'bold'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: true
|
|
|
+ },
|
|
|
+ data: [
|
|
|
+ { value: 35, name: 'v3.2.1' },
|
|
|
+ { value: 25, name: 'v3.1.5' },
|
|
|
+ { value: 20, name: 'v3.0.8' },
|
|
|
+ { value: 10, name: 'v2.9.3' },
|
|
|
+ { value: 10, name: '其他' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ circleChart1.setOption(option);
|
|
|
+}
|
|
|
+// 初始化圆环图
|
|
|
+function initCircleChart2(): void {
|
|
|
+ if (!circleEchartRef2.value) return;
|
|
|
+ if (circleChart2) circleChart2.dispose();
|
|
|
+
|
|
|
+ circleChart2 = echarts.init(circleEchartRef2.value);
|
|
|
+
|
|
|
+ const option: echarts.EChartsOption = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ bottom: '0',
|
|
|
+ left: 'center'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '版本分布',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['50%', '70%'], // 设置内半径和外半径,形成圆环效果
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ padAngle: 2, // 扇区间隙
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 5 // 扇区圆角
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ formatter: '{b}: {d}%' // 显示标签和百分比
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ fontSize: 14,
|
|
|
+ fontWeight: 'bold'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: true
|
|
|
+ },
|
|
|
+ data: [
|
|
|
+ { value: 35, name: 'v3.2.1' },
|
|
|
+ { value: 25, name: 'v3.1.5' },
|
|
|
+ { value: 20, name: 'v3.0.8' },
|
|
|
+ { value: 10, name: 'v2.9.3' },
|
|
|
+ { value: 10, name: '其他' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ };
|
|
|
+
|
|
|
+ circleChart2.setOption(option);
|
|
|
+}
|
|
|
|
|
|
|
|
|
</script>
|
|
@@ -235,8 +423,9 @@ function initQualityChart(): void {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
.line {
|
|
|
- margin: 90px -30px 30px;
|
|
|
+ margin: 60px -30px 30px;
|
|
|
height: 1px;
|
|
|
background-color: #E6E6E6;
|
|
|
}
|