|
|
@@ -0,0 +1,215 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <div class="period-switch">
|
|
|
+ <el-radio-group v-model="periodType" @change="handlePeriodChange">
|
|
|
+ <el-radio-button label="current">本月</el-radio-button>
|
|
|
+ <el-radio-button label="last">上月</el-radio-button>
|
|
|
+ </el-radio-group>
|
|
|
+ <span class="period-label">{{ periodLabel }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-row :gutter="20" class="card-row">
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="metric-card">
|
|
|
+ <div class="metric-icon" style="background: #409EFF;">
|
|
|
+ <i class="el-icon-user"></i>
|
|
|
+ </div>
|
|
|
+ <div class="metric-content">
|
|
|
+ <div class="metric-value">{{ reportData.newUsers || 0 }}</div>
|
|
|
+ <div class="metric-label">新增用户数</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="metric-card">
|
|
|
+ <div class="metric-icon" style="background: #67C23A;">
|
|
|
+ <i class="el-icon-s-custom"></i>
|
|
|
+ </div>
|
|
|
+ <div class="metric-content">
|
|
|
+ <div class="metric-value">{{ reportData.totalUsers || 0 }}</div>
|
|
|
+ <div class="metric-label">累计注册用户</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="metric-card">
|
|
|
+ <div class="metric-icon" style="background: #E6A23C;">
|
|
|
+ <i class="el-icon-s-data"></i>
|
|
|
+ </div>
|
|
|
+ <div class="metric-content">
|
|
|
+ <div class="metric-value">{{ reportData.activeUsers || 0 }}</div>
|
|
|
+ <div class="metric-label">活跃用户数</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="metric-card">
|
|
|
+ <div class="metric-icon" style="background: #F56C6C;">
|
|
|
+ <i class="el-icon-connection"></i>
|
|
|
+ </div>
|
|
|
+ <div class="metric-content">
|
|
|
+ <div class="metric-value">{{ reportData.retentionRate != null ? reportData.retentionRate + '%' : '-' }}</div>
|
|
|
+ <div class="metric-label">留存率</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20" class="card-row">
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="metric-card">
|
|
|
+ <div class="metric-icon" style="background: #909399;">
|
|
|
+ <i class="el-icon-time"></i>
|
|
|
+ </div>
|
|
|
+ <div class="metric-content">
|
|
|
+ <div class="metric-value">{{ reportData.avgUseDuration != null ? reportData.avgUseDuration + ' 分钟' : '-' }}</div>
|
|
|
+ <div class="metric-label">用户平均使用时长</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="metric-card">
|
|
|
+ <div class="metric-icon" style="background: #9C27B0;">
|
|
|
+ <i class="el-icon-coin"></i>
|
|
|
+ </div>
|
|
|
+ <div class="metric-content">
|
|
|
+ <div class="metric-value">{{ reportData.ltv != null ? '¥' + reportData.ltv : '-' }}</div>
|
|
|
+ <div class="metric-label">用户生命周期价值(LTV)</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="metric-card">
|
|
|
+ <div class="metric-icon" style="background: #FF9800;">
|
|
|
+ <i class="el-icon-money"></i>
|
|
|
+ </div>
|
|
|
+ <div class="metric-content">
|
|
|
+ <div class="metric-value">{{ reportData.cac != null ? '¥' + reportData.cac : '-' }}</div>
|
|
|
+ <div class="metric-label">用户获取成本(CAC)</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card shadow="hover" class="metric-card">
|
|
|
+ <div class="metric-icon" style="background: #795548;">
|
|
|
+ <i class="el-icon-warning-outline"></i>
|
|
|
+ </div>
|
|
|
+ <div class="metric-content">
|
|
|
+ <div class="metric-value">{{ reportData.monthlyChurnRate != null ? reportData.monthlyChurnRate + '%' : '-' }}</div>
|
|
|
+ <div class="metric-label">月流失率</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import request from '@/utils/request'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: "AppOperationReport",
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ periodType: "current",
|
|
|
+ reportData: {},
|
|
|
+ year: null,
|
|
|
+ month: null
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ periodLabel() {
|
|
|
+ return this.year + '年' + this.month + '月';
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.initPeriod();
|
|
|
+ this.getData();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ initPeriod() {
|
|
|
+ const now = new Date();
|
|
|
+ if (this.periodType === 'current') {
|
|
|
+ this.year = now.getFullYear();
|
|
|
+ this.month = now.getMonth() + 1;
|
|
|
+ } else {
|
|
|
+ const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
|
|
+ this.year = lastMonth.getFullYear();
|
|
|
+ this.month = lastMonth.getMonth() + 1;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handlePeriodChange() {
|
|
|
+ this.initPeriod();
|
|
|
+ this.getData();
|
|
|
+ },
|
|
|
+ getData() {
|
|
|
+ request({
|
|
|
+ url: '/his/appOperationReport/monthReport',
|
|
|
+ method: 'get',
|
|
|
+ params: {
|
|
|
+ year: this.year,
|
|
|
+ month: this.month
|
|
|
+ }
|
|
|
+ }).then(response => {
|
|
|
+ this.reportData = response.data || {};
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.period-switch {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.period-label {
|
|
|
+ margin-left: 20px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+.card-row {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+.metric-card {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+.metric-card >>> .el-card__body {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
+.metric-icon {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ border-radius: 12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+.metric-icon i {
|
|
|
+ font-size: 28px;
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+.metric-content {
|
|
|
+ margin-left: 20px;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+.metric-value {
|
|
|
+ font-size: 24px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+ line-height: 1.2;
|
|
|
+}
|
|
|
+.metric-label {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #909399;
|
|
|
+ margin-top: 6px;
|
|
|
+}
|
|
|
+</style>
|