Browse Source

feat:渠道分析,其他渠道页面编写

luoy 19 hours ago
parent
commit
059121b101

+ 76 - 0
src/components/LYcom/Lprogress/index.vue

@@ -0,0 +1,76 @@
+<template>
+  <div class="l-progress">
+    <span class="l-progress-label">{{ label }}</span>
+    <div class="l-progress-bar-container">
+      <div 
+        class="l-progress-bar" 
+        :style="{ width: percentage + '%' }"
+      ></div>
+    </div>
+    <span class="l-progress-value">{{ percentage }}%</span>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue'
+
+// 定义组件props
+interface Props {
+  label: string
+  num: number
+  count: number
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  label: '',
+  num: 0,
+  count: 0
+})
+
+// 计算占比百分比
+const percentage = computed(() => {
+  if (props.count === 0) return 0
+  return Math.min(100, Math.max(0, (props.num / props.count) * 100))
+})
+</script>
+
+<style scoped lang="scss">
+.l-progress {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  
+  .l-progress-label {
+    font-size: 14px;
+    color: #121212;
+    white-space: nowrap;
+    margin-right: 12px;
+    display: inline-block;
+    width: 80px;
+    text-align: right;
+  }
+  
+  .l-progress-bar-container {
+    flex: 1;
+    height: 10px;
+    background-color: #e4e7ed;
+    border-radius: 5px;
+    overflow: hidden;
+    margin-right: 12px;
+    
+    .l-progress-bar {
+      height: 100%;
+      background: linear-gradient(90deg, #409eff, #66b1ff);
+      border-radius: 5px;
+      transition: width 0.3s ease;
+    }
+  }
+  
+  .l-progress-value {
+    font-size: 14px;
+    color: #121212;
+    font-weight: 500;
+    white-space: nowrap;
+  }
+}
+</style>

+ 393 - 0
src/views/count/channel/allChannel.vue

@@ -0,0 +1,393 @@
+<template>
+  <div class="layout-padding">
+    <div class="!overflow-auto px-1">
+      <Lcard :height="150">
+        <template #default>
+          <div class="box1">
+            <Title style="margin-bottom: 10px;" title="渠道分析">
+              <el-popover class="box-item" placement="right" trigger="hover" width="250">
+                <template #reference>
+                  <el-icon class="ml-1" style="color: #a4b8cf">
+                    <InfoFilled />
+                  </el-icon>
+                </template>
+                <template #default>
+                  <div class="ant-popover-inner-content">
+                    渠道分析指您投放在各大应用市场的渠道包,您可通过当前页面了解各渠道的活跃度、新增用户排名和渠道价值。
+                  </div>
+                </template>
+              </el-popover>
+            </Title>
+            <span class="link">查看完整AI简报</span>
+          </div>
+          <div class="box1" style="margin-top: 20px;">
+            <el-select v-model="selectChannel" style="width: 200px;">
+              <el-option label="全部渠道" value="1"></el-option>
+              <el-option label="安卓" value="2"></el-option>
+              <el-option label="苹果" value="3"></el-option>
+            </el-select>
+            <div class="box1-time">
+              <el-radio-group v-model="timeGroup">
+                <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>
+              <el-date-picker style="float: left;" v-model="timeRange" type="datetimerange" range-separator="To"
+                start-placeholder="Start date" end-placeholder="End date" />
+            </div>
+          </div>
+        </template>
+      </Lcard>
+      <Lcard :height="550">
+        <Title left-line title="渠道趋势对比">
+        </Title>
+        <div class="flex items-center justify-between mb-2 mt-3" style="margin-top: 20px;margin-left: 60px;">
+          <div class="flex items-center">
+            <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-button label="month">新增次日留存率</el-radio-button>
+            </el-radio-group>
+          </div>
+          <div class="flex items-center">
+            <el-select v-model="selectVersion" style="width: 200px;">
+              <el-option label="全部版本" value="1"></el-option>
+              <el-option label="1.0.1" value="2"></el-option>
+              <el-option label="1.02" value="3"></el-option>
+            </el-select>
+          </div>
+        </div>
+        <el-select v-model="selectLineChannel" multiple clearable style="width: 400px;margin: 20px 60px;">
+          <el-option label="App Store" value="1"></el-option>
+          <el-option label="应用宝" value="2"></el-option>
+          <el-option label="安卓" value="3"></el-option>
+        </el-select>
+        <div class="relative">
+          <div ref="qChartRef" style="width: 100%; height: 320px"></div>
+        </div>
+      </Lcard>
+      <Lcard :height="650">
+        <Title left-line title="渠道矩阵分析">
+          <el-popover class="box-item" placement="right" trigger="hover" width="250">
+            <template #reference>
+              <el-icon class="ml-1" style="color: #a4b8cf">
+                <InfoFilled />
+              </el-icon>
+            </template>
+            <template #default>
+              <div class="ant-popover-inner-content">
+                <div class="um-page-tips-content" style="line-height: 24px">
+                  可通过矩阵分析了解各个渠道的价值,在您推广平台选择时也可选择高转化高偏好的渠道作为重点渠道
+                </div>
+              </div>
+            </template>
+          </el-popover>
+        </Title>
+        <div style="margin: 30px 60px">
+          <span>横轴:新增用户(日均)</span>
+          纵轴:<el-select v-model="selectSanDian" style="width: 200px;display: inline-block;">
+            <el-option label="次日留存率(日均)" value="1"></el-option>
+            <el-option label="后日留存率(日均)" value="2"></el-option>
+          </el-select>
+        </div>
+        <div ref="SDChartRef" style="width: 100%; height: 480px"></div>
+      </Lcard>
+      <Lcard>
+        <Title left-line title="渠道明细数据">
+        </Title>
+        <div class="box1" style="margin: 30px 60px">
+          <div class="flex items-center justify-between">
+            <el-radio-group v-model="radioTableDay">
+              <el-radio-button label="today">今日</el-radio-button>
+              <el-radio-button label="yesterday">昨日</el-radio-button>
+            </el-radio-group>
+            <el-select class="ml-2" v-model="tableChannel" style="width: 140px" placeholder="渠道选择">
+              <el-option label="全部" value="1"></el-option>
+              <el-option label="未分组" value="2"></el-option>
+            </el-select>
+          </div>
+          <div class="link">导出</div>
+        </div>
+        <el-table :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>
+      </Lcard>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+// import { ref, onMounted, watch, computed, defineAsyncComponent } from 'vue';
+import { useI18n } from 'vue-i18n';
+import * as echarts from 'echarts';
+// 引入组件
+const Lcard = defineAsyncComponent(() => import('/@/components/LYcom/Lcard/index.vue'));
+const Title = defineAsyncComponent(() => import('/@/components/Title/index.vue'));
+const { t } = useI18n();
+
+const props = defineProps({
+  channel: {
+    type: String,
+    default: '1',
+  },
+});
+const emit = defineEmits(['update:channel'])
+
+// 渠道选择
+const selectChannel = computed({
+  get() {
+    return props.channel;
+  },
+  set(val) {
+    emit('update:channel', val);
+    console.log(val);
+  },
+})
+
+const timeGroup = ref('1')
+const timeRange: any = ref(null);
+watch(timeGroup, (newVal) => {
+  const now = new Date();
+  let start: Date;
+  let end: Date = new Date(now);
+
+  switch (newVal) {
+    case 'day':
+      // 设置为今天 00:00:00 到 23:59:59
+      start = new Date(now);
+      start.setHours(0, 0, 0, 0);
+      end.setHours(23, 59, 59, 999);
+      break;
+    case 'week':
+      // 设置为本周第一天(周日)到本周最后一天(周六)
+      start = new Date(now);
+      const day = start.getDay();
+      const diff = start.getDate() - day;
+      start.setDate(diff);
+      start.setHours(0, 0, 0, 0);
+      end = new Date(start);
+      end.setDate(start.getDate() + 6);
+      end.setHours(23, 59, 59, 999);
+      break;
+    case 'month':
+      // 设置为本月第一天到本月最后一天
+      start = new Date(now.getFullYear(), now.getMonth(), 1);
+      start.setHours(0, 0, 0, 0);
+      end = new Date(now.getFullYear(), now.getMonth() + 1, 0);
+      end.setHours(23, 59, 59, 999);
+      break;
+    default:
+      // 其他情况清空时间范围
+      timeRange.value = null;
+      return;
+  }
+
+  timeRange.value = [start, end];
+}, { immediate: true });
+
+const selectVersion = ref('1') // 选择版本
+
+const selectLineChannel = ref([]) // 选择线形图渠道
+
+const selectSanDian = ref('1') // 选择散点图纵坐标
+
+const radioTableDay = ref('1') // 选择表格时间
+const tableChannel = ref('1') // 选择表格渠道
+
+let qualityChart: echarts.ECharts | null = null;
+const qChartRef = ref(null);
+const lineChartUser = ref(null);
+onMounted(() => {
+  setTimeout(() => {
+    initQualityChart();
+    initScatterChart();
+  }, 500)
+});
+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',
+]);
+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, 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 SDChartRef = ref(null);
+let scatterChart: echarts.ECharts | null = null;
+
+// 散点图数据
+const scatterData = ref([
+  { name: 'App Store', x: 120, y: 45.2, symbolSize: 80 },
+  { name: '应用宝', x: 210, y: 38.7, symbolSize: 120 },
+  { name: '华为应用市场', x: 98, y: 32.5, symbolSize: 65 },
+  { name: '小米应用商店', x: 180, y: 41.3, symbolSize: 110 },
+  { name: 'OPPO软件商店', x: 156, y: 36.8, symbolSize: 95 },
+  { name: 'vivo应用商店', x: 134, y: 34.2, symbolSize: 85 },
+  { name: '360手机助手', x: 178, y: 39.1, symbolSize: 105 },
+  { name: '应用汇', x: 89, y: 28.6, symbolSize: 60 },
+  { name: '安卓市场', x: 145, y: 35.7, symbolSize: 90 },
+  { name: '91助手', x: 112, y: 31.4, symbolSize: 70 }
+]);
+
+// 初始化散点图
+function initScatterChart(): void {
+  if (!SDChartRef.value) return;
+  if (scatterChart) scatterChart.dispose();
+
+  scatterChart = echarts.init(SDChartRef.value);
+
+  const option: echarts.EChartsOption = {
+    tooltip: {
+      trigger: 'item',
+      formatter: (params: any) => {
+        return `${params.name}<br/>新增用户: ${params.data.x}<br/>留存率: ${params.data.y}%`;
+      }
+    },
+    grid: {
+      left: '7%',
+      right: '12%',
+      top: '10%',
+      bottom: '10%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'value',
+      name: '新增用户(日均)',
+      scale: true,
+      axisLine: {
+        show: true
+      },
+      splitLine: {
+        show: false
+      }
+    },
+    yAxis: {
+      type: 'value',
+      name: '次日留存率(日均)',
+      scale: true,
+      axisLine: {
+        show: true
+      },
+      splitLine: {
+        show: true
+      },
+      axisLabel: {
+        formatter: '{value}%'
+      }
+    },
+    series: [{
+      type: 'scatter',
+      symbolSize: (data: any) => data[2],
+      data: scatterData.value.map(item => [item.x, item.y, item.symbolSize, item.name]),
+      itemStyle: {
+        color: (params: any) => {
+          const colors = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'];
+          return colors[params.dataIndex % colors.length];
+        }
+      },
+      label: {
+        show: true,
+        formatter: (params: any) => params.data[3],
+        position: 'inside',
+        fontSize: 10,
+        color: '#fff'
+      }
+    }]
+  };
+
+  scatterChart.setOption(option);
+}
+
+
+</script>
+<style lang="scss" scoped>
+.link {
+  color: #167af0;
+  cursor: pointer;
+}
+
+.box1 {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.line {
+  margin: 60px -30px 30px;
+  height: 1px;
+  background-color: #E6E6E6;
+}
+</style>

+ 10 - 0
src/views/count/channel/index.vue

@@ -0,0 +1,10 @@
+<template>
+  <AllChannel v-if="channel === '1'" v-model:channel="channel"></AllChannel>
+  <OtherChannel v-else v-model:channel="channel"></OtherChannel>
+</template>
+<script setup lang="ts" name="countMainTrend">
+const AllChannel = defineAsyncComponent(() => import('./allChannel.vue'));
+const OtherChannel = defineAsyncComponent(() => import('./otherChannel.vue'));
+
+const channel = ref('2')
+</script>

+ 575 - 0
src/views/count/channel/otherChannel.vue

@@ -0,0 +1,575 @@
+<template>
+  <div class="layout-padding">
+    <div class="!overflow-auto px-1">
+      <Lcard>
+        <div class="flex justify-start items-center">
+          <el-icon class="ml-1" style="color: #333333; margin-right: 25px">
+            <ArrowLeftBold />
+          </el-icon>
+          <el-select v-model="selectChannel" style="width: 100px;">
+            <el-option label="全部渠道" value="1"></el-option>
+            <el-option label="安卓" value="2"></el-option>
+            <el-option label="苹果" value="3"></el-option>
+          </el-select>
+        </div>
+      </Lcard>
+      <Lcard>
+        <div class="flex justify-between items-center">
+          <Title left-line title="渠道趋势">
+          </Title>
+          <div class="box1">
+            <el-select v-model="selectVersion" style="width: 140px; margin-right: 30px;">
+              <el-option label="全部版本" value="1"></el-option>
+              <el-option label="1.0.1" value="2"></el-option>
+              <el-option label="1.02" value="3"></el-option>
+            </el-select>
+            <div class="box1-time">
+              <el-date-picker style="float: left; width: 240px; margin-right: 30px;" v-model="timeRange"
+                type="datetimerange" range-separator="To" start-placeholder="Start date" end-placeholder="End date" />
+              <el-radio-group v-model="timeGroup" style="width: 240px; margin-right: 30px;">
+                <el-radio-button label="week">过去七天</el-radio-button>
+                <el-radio-button label="month">过去三十天</el-radio-button>
+              </el-radio-group>
+            </div>
+            <div class="link">导出数据
+              <span style="display: inline-block; transform:translateY(3px);">
+                <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+                  <g clip-path="url(#clip0_713_1665)">
+                    <path
+                      d="M14 1.33301H1.99992C1.63172 1.33301 1.33324 1.63149 1.33325 1.99969L1.33358 13.9997C1.33359 14.3679 1.63207 14.6663 2.00025 14.6663H14C14.3682 14.6663 14.6667 14.3679 14.6667 13.9997V1.99967C14.6667 1.63148 14.3682 1.33301 14 1.33301Z"
+                      stroke="#167AF0" />
+                    <path d="M6.66968 11.3366H11.1669V6.66992" stroke="#167AF0" stroke-linecap="round"
+                      stroke-linejoin="round" />
+                    <path d="M9.66675 8.16699L10.1667 7.66699L11.1667 6.66699L12.1667 7.66699L12.6667 8.16699"
+                      stroke="#167AF0" stroke-linecap="round" stroke-linejoin="round" />
+                    <path d="M4.66675 1.33301V14.6663" stroke="#167AF0" stroke-linecap="round" />
+                    <path d="M1.33325 4.67959L14.6666 4.66699" stroke="#167AF0" stroke-linecap="round" />
+                    <path d="M2.66675 1.33301H9.33341" stroke="#167AF0" stroke-linecap="round"
+                      stroke-linejoin="round" />
+                    <path d="M2.66675 14.667H9.33341" stroke="#167AF0" stroke-linecap="round" stroke-linejoin="round" />
+                    <path d="M14.6667 2.66699V6.00033" stroke="#167AF0" stroke-linecap="round" />
+                    <path d="M1.33325 2.66699V6.00033" stroke="#167AF0" stroke-linecap="round" />
+                  </g>
+                  <defs>
+                    <clipPath id="clip0_713_1665">
+                      <rect width="16" height="16" fill="white" />
+                    </clipPath>
+                  </defs>
+                </svg>
+              </span>
+            </div>
+          </div>
+        </div>
+        <div class="line" style="margin: 35px -30px 40px;"></div>
+        <div class="flex items-center justify-between mb-2 mt-3" style="margin-top: 20px;margin-left: 60px;">
+          <div class="flex items-center">
+            <el-radio-group v-model="lineChartUser">
+              <el-radio-button label="1">新增用户</el-radio-button>
+              <el-radio-button label="2">活跃用户</el-radio-button>
+              <el-radio-button label="3">启动次数</el-radio-button>
+              <el-radio-button label="4">平均单次使用时长</el-radio-button>
+              <el-radio-button label="5">新增次日留存率</el-radio-button>
+            </el-radio-group>
+          </div>
+        </div>
+        <el-select placeholder="对比分析" v-model="selectLineChannel" multiple clearable
+          style="width: 140px;margin: 20px 60px;">
+          <el-option label="时段对比" value="1"></el-option>
+          <el-option label="渠道对比" value="2"></el-option>
+          <el-option label="版本对比" value="3"></el-option>
+        </el-select>
+        <div class="relative">
+          <div ref="qChartRef" style="margin-left: 60px; width: calc(100% - 80px); height: 320px"></div>
+        </div>
+        <div class="mt-3" style="margin-top: 20px;margin-left: 60px;">
+          <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>
+      <div class="flex justify-between">
+        <Lcard style="width: calc(50% - 5px);">
+          <div class="flex justify-between items-center">
+            <Title left-line title="渠道趋势">
+              <el-popover class="box-item" placement="right" trigger="hover" width="250">
+                <template #reference>
+                  <el-icon class="ml-1" style="color: #a4b8cf">
+                    <InfoFilled />
+                  </el-icon>
+                </template>
+                <template #default>
+                  <div class="ant-popover-inner-content">
+                    渠道活跃度,展示指定渠道用户的昨日活跃/过去7天活跃、昨日活跃/过去30天活跃的信息。<br>
+                    通过这两个指标,您可以了解到该渠道用户的粘着度。昨日活跃/过去30天活跃越接入100%,<br>
+                    用户越活跃,流失率越低,粘性越强这里的活跃用户是去重后的活跃用户
+                  </div>
+                </template>
+              </el-popover>
+            </Title>
+            <div style="font-size: 15px; color: #646464;">
+              2025/08/19
+            </div>
+          </div>
+          <div class="flex justify-between items-center">
+            <div ref="qdqsRef1" style="width: 48%; height: 220px;"></div>
+            <div ref="qdqsRef2" style="width: 48%; height: 220px;"></div>
+          </div>
+        </Lcard>
+        <Lcard style="width: calc(50% - 5px);">
+          <div class="flex justify-between items-center">
+            <Title left-line title="新增用户留存率">
+              <el-popover class="box-item" placement="right" trigger="hover" width="250">
+                <template #reference>
+                  <el-icon class="ml-1" style="color: #a4b8cf">
+                    <InfoFilled />
+                  </el-icon>
+                </template>
+                <template #default>
+                  <div class="ant-popover-inner-content">
+                    展示了渠道近期的留存率情况,可以帮助您了解该渠道用户的忠诚度<br>
+                    次日留存率: 某日的新增用户在次日启动过应用的比例<br>
+                    7日留存率: 某日的新增用户在7天后启动过应用的比例<br>
+                    14日留存率: 某日的新增用户在14天后启动过应用的比例<br>
+                    这里展示的是渠道前天在昨天的次日留存率、8天前在昨天的7日留存率和15天前在昨天的14日留存率
+                  </div>
+                </template>
+              </el-popover>
+            </Title>
+            <div style="font-size: 15px; color: #646464;">
+              2025/08/19
+            </div>
+          </div>
+          <Lprogress style="margin-top: 45px;" v-for="i in progressArray" :key="i.label" :label="i.label" :num="i.num" :count="i.count"></Lprogress>
+        </Lcard>
+      </div>
+      <Lcard :height="650">
+        <Title left-line title="渠道矩阵分析">
+          <el-popover class="box-item" placement="right" trigger="hover" width="250">
+            <template #reference>
+              <el-icon class="ml-1" style="color: #a4b8cf">
+                <InfoFilled />
+              </el-icon>
+            </template>
+            <template #default>
+              <div class="ant-popover-inner-content">
+                <div class="um-page-tips-content" style="line-height: 24px">
+                  可通过矩阵分析了解各个渠道的价值,在您推广平台选择时也可选择高转化高偏好的渠道作为重点渠道
+                </div>
+              </div>
+            </template>
+          </el-popover>
+        </Title>
+        <div style="margin: 30px 60px">
+          <span>横轴:新增用户(日均)</span>
+          纵轴:<el-select v-model="selectSanDian" style="width: 200px;display: inline-block;">
+            <el-option label="次日留存率(日均)" value="1"></el-option>
+            <el-option label="后日留存率(日均)" value="2"></el-option>
+          </el-select>
+        </div>
+        <div ref="SDChartRef" style="width: 100%; height: 480px"></div>
+      </Lcard>
+      <Lcard>
+        <Title left-line title="渠道明细数据">
+        </Title>
+        <div class="box1" style="margin: 30px 60px">
+          <div class="flex items-center justify-between">
+            <el-radio-group v-model="radioTableDay">
+              <el-radio-button label="today">今日</el-radio-button>
+              <el-radio-button label="yesterday">昨日</el-radio-button>
+            </el-radio-group>
+            <el-select class="ml-2" v-model="tableChannel" style="width: 140px" placeholder="渠道选择">
+              <el-option label="全部" value="1"></el-option>
+              <el-option label="未分组" value="2"></el-option>
+            </el-select>
+          </div>
+          <div class="link">导出</div>
+        </div>
+        <el-table :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>
+      </Lcard>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+// import { ref, onMounted, watch, computed, defineAsyncComponent } from 'vue';
+import { useI18n } from 'vue-i18n';
+import * as echarts from 'echarts';
+// 引入组件
+const Lcard = defineAsyncComponent(() => import('/@/components/LYcom/Lcard/index.vue'));
+const Title = defineAsyncComponent(() => import('/@/components/Title/index.vue'));
+const Lprogress = defineAsyncComponent(() => import('/@/components/LYcom/Lprogress/index.vue'));
+const { t } = useI18n();
+
+const selectChannel = ref('1') // 渠道选择
+const timeGroup = ref('1')
+const timeRange: any = ref(null);
+watch(timeGroup, (newVal) => {
+  const now = new Date();
+  let start: Date;
+  let end: Date = new Date(now);
+
+  switch (newVal) {
+    case 'day':
+      // 设置为今天 00:00:00 到 23:59:59
+      start = new Date(now);
+      start.setHours(0, 0, 0, 0);
+      end.setHours(23, 59, 59, 999);
+      break;
+    case 'week':
+      // 设置为本周第一天(周日)到本周最后一天(周六)
+      start = new Date(now);
+      const day = start.getDay();
+      const diff = start.getDate() - day;
+      start.setDate(diff);
+      start.setHours(0, 0, 0, 0);
+      end = new Date(start);
+      end.setDate(start.getDate() + 6);
+      end.setHours(23, 59, 59, 999);
+      break;
+    case 'month':
+      // 设置为本月第一天到本月最后一天
+      start = new Date(now.getFullYear(), now.getMonth(), 1);
+      start.setHours(0, 0, 0, 0);
+      end = new Date(now.getFullYear(), now.getMonth() + 1, 0);
+      end.setHours(23, 59, 59, 999);
+      break;
+    default:
+      // 其他情况清空时间范围
+      timeRange.value = null;
+      return;
+  }
+
+  timeRange.value = [start, end];
+}, { immediate: true });
+
+const selectVersion = ref('1') // 选择版本
+
+const selectLineChannel = ref([]) // 选择线形图渠道
+
+const selectSanDian = ref('1') // 选择散点图纵坐标
+
+const radioTableDay = ref('1') // 选择表格时间
+const tableChannel = ref('1') // 选择表格渠道
+
+let qualityChart: echarts.ECharts | null = null;
+const qChartRef = ref(null);
+const lineChartUser = ref(null);
+onMounted(() => {
+  setTimeout(() => {
+    initQualityChart();
+    initChart1();
+    initChart2();
+  }, 500)
+});
+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',
+]);
+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, 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 qdqsRef1 = ref(null);
+const qdqsCount1 = ref(85);
+const qdqsRef2 = ref(null);
+const qdqsCount2 = ref(65);
+let chart1: echarts.ECharts | null = null;
+let chart2: echarts.ECharts | null = null;
+
+// 初始化第一个饼图进度条
+function initChart1(): void {
+  if (!qdqsRef1.value) return;
+  if (chart1) chart1.dispose();
+  
+  chart1 = echarts.init(qdqsRef1.value);
+  
+  const option: echarts.EChartsOption = {
+    series: [
+      {
+        type: 'pie',
+        radius: ['60%', '70%'],
+        center: ['50%', '50%'],
+        avoidLabelOverlap: false,
+        startAngle: 270,
+        endAngle: 270 - (qdqsCount1.value / 100 * 360),
+        silent: true,
+        itemStyle: {
+          color: '#167AF0',
+          borderRadius: 10
+        },
+        data: [100],
+        label: {
+          show: false
+        },
+        emphasis: {
+          disabled: true
+        },
+      },
+      {
+        type: 'pie',
+        radius: ['60%', '70%'],
+        center: ['50%', '50%'],
+        avoidLabelOverlap: false,
+        startAngle: 360,
+        endAngle: 360,
+        itemStyle: {
+          color: '#409eff'
+        },
+        data: [65, 35],
+        label: {
+          show: false
+        },
+        emphasis: {
+          disabled: true
+        }
+      }
+    ],
+    graphic: {
+      elements: [
+        {
+          type: 'text',
+          key: 'percent-text',
+          z: 100,
+          left: 'center',
+          top: 'center',
+          style: {
+            text: qdqsCount1.value + '%',
+            fontSize: 24,
+            fontWeight: 'bold',
+            fill: '#121212'
+          }
+        },
+        {
+          type: 'text',
+          key: 'label-text',
+          z: 100,
+          left: 'center',
+          bottom: '3%',
+          style: {
+            text: '昨日活跃/过去7天活跃',
+            fontSize: 14,
+            fill: '#121212'
+          }
+        }
+      ]
+    }
+  };
+  
+  chart1.setOption(option);
+}
+
+// 初始化第二个饼图进度条
+function initChart2(): void {
+  if (!qdqsRef2.value) return;
+  if (chart2) chart2.dispose();
+  
+  chart2 = echarts.init(qdqsRef2.value);
+  
+  const option: echarts.EChartsOption = {
+    series: [
+      {
+        type: 'pie',
+        radius: ['60%', '70%'],
+        center: ['50%', '50%'],
+        avoidLabelOverlap: false,
+        startAngle: 270,
+        endAngle: 270 - (qdqsCount2.value / 100 * 360),
+        silent: true,
+        itemStyle: {
+          color: '#00BC71',
+          borderRadius: 10
+        },
+        data: [100],
+        label: {
+          show: false
+        },
+        emphasis: {
+          disabled: true
+        },
+      },
+      {
+        type: 'pie',
+        radius: ['60%', '70%'],
+        center: ['50%', '50%'],
+        avoidLabelOverlap: false,
+        startAngle: 360,
+        endAngle: 360,
+        itemStyle: {
+          color: '#409eff'
+        },
+        data: [65, 35],
+        label: {
+          show: false
+        },
+        emphasis: {
+          disabled: true
+        }
+      }
+    ],
+    graphic: {
+      elements: [
+        {
+          type: 'text',
+          key: 'percent-text',
+          z: 100,
+          left: 'center',
+          top: 'center',
+          style: {
+            text: qdqsCount2.value + '%',
+            fontSize: 24,
+            fontWeight: 'bold',
+            fill: '#121212'
+          }
+        },
+        {
+          type: 'text',
+          key: 'label-text',
+          z: 100,
+          left: 'center',
+          bottom: '3%',
+          style: {
+            text: '昨日活跃/过去30天活跃',
+            fontSize: 14,
+            fill: '#121212'
+          }
+        }
+      ]
+    }
+  };
+  
+  chart2.setOption(option);
+}
+
+
+// 横向进度条相关
+const progressArray = ref([
+  {
+    label: '次日留存率',
+    num: 80,
+    count: 200
+  },
+  {
+    label: '7日留存率',
+    num: 80,
+    count: 200
+  },
+  {
+    label: '14日留存率',
+    num: 40,
+    count: 200
+  },
+])
+
+</script>
+<style lang="scss" scoped>
+.link {
+  color: #167af0;
+  cursor: pointer;
+}
+
+.box1 {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.line {
+  margin: 60px -30px 30px;
+  height: 1px;
+  background-color: #E6E6E6;
+}
+</style>

+ 9 - 0
src/views/count/main/trend/icons/icon1.svg

@@ -1,3 +1,12 @@
 <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
 <rect width="48" height="48" rx="14" fill="#167AF0" fill-opacity="0.1"/>
+<g clip-path="url(#clip0_554_3349)">
+<path d="M24 23.2507C25.0652 23.2507 26.1064 22.9348 26.9921 22.3431C27.8778 21.7513 28.568 20.9102 28.9757 19.9261C29.3833 18.942 29.4899 17.8591 29.2821 16.8144C29.0743 15.7697 28.5614 14.8101 27.8082 14.0569C27.055 13.3037 26.0954 12.7908 25.0507 12.583C24.006 12.3752 22.9231 12.4818 21.939 12.8894C20.955 13.2971 20.1138 13.9874 19.5221 14.873C18.9303 15.7587 18.6144 16.7999 18.6144 17.8651C18.6144 19.2934 19.1818 20.6633 20.1918 21.6733C21.2018 22.6833 22.5717 23.2507 24 23.2507ZM30.7824 25.4395C29.0628 24.187 27.03 23.4351 24.9093 23.2672C22.7886 23.0993 20.6628 23.5219 18.7675 24.4882C16.8723 25.4544 15.2816 26.9266 14.1718 28.7416C13.062 30.5565 12.4764 32.6433 12.48 34.7707V35.5195H27.4272C26.5728 34.7725 25.958 33.7898 25.6602 32.6946C25.3623 31.5994 25.3947 30.4407 25.7531 29.3638C26.1115 28.287 26.7801 27.34 27.6749 26.6419C28.5697 25.9437 29.6507 25.5254 30.7824 25.4395Z" fill="#167AF0"/>
+<path d="M34.08 29.7594H32.64V28.3194C32.64 27.9375 32.4883 27.5712 32.2183 27.3012C31.9482 27.0311 31.5819 26.8794 31.2 26.8794C30.8181 26.8794 30.4519 27.0311 30.1818 27.3012C29.9117 27.5712 29.76 27.9375 29.76 28.3194V29.7594H28.32C27.9381 29.7594 27.5719 29.9111 27.3018 30.1812C27.0317 30.4512 26.88 30.8175 26.88 31.1994C26.88 31.5813 27.0317 31.9476 27.3018 32.2176C27.5719 32.4877 27.9381 32.6394 28.32 32.6394H29.76V34.0794C29.76 34.4613 29.9117 34.8276 30.1818 35.0976C30.4519 35.3677 30.8181 35.5194 31.2 35.5194C31.5819 35.5194 31.9482 35.3677 32.2183 35.0976C32.4883 34.8276 32.64 34.4613 32.64 34.0794V32.6394H34.08C34.4619 32.6394 34.8282 32.4877 35.0983 32.2176C35.3683 31.9476 35.52 31.5813 35.52 31.1994C35.52 30.8175 35.3683 30.4512 35.0983 30.1812C34.8282 29.9111 34.4619 29.7594 34.08 29.7594Z" fill="#167AF0"/>
+</g>
+<defs>
+<clipPath id="clip0_554_3349">
+<rect width="23.04" height="23.04" fill="white" transform="translate(12.48 12.4795)"/>
+</clipPath>
+</defs>
 </svg>

File diff suppressed because it is too large
+ 1 - 0
src/views/count/main/trend/icons/icon2.svg


+ 1 - 1
src/views/count/main/trend/index.vue

@@ -92,7 +92,7 @@
                </div>
             </template>
          </Lcard>
-         <Lcard :height="975">
+         <Lcard>
             <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>

Some files were not shown because too many files changed in this diff