Pārlūkot izejas kodu

Merge branch 'dev-jcq' of https://s-20coaj910c.zht2.com/cmy/data-marketing-platform into dev-ly

luoy 2 dienas atpakaļ
vecāks
revīzija
416839c4d2

+ 1 - 1
src/layout/navMenu/subItem.vue

@@ -8,7 +8,7 @@
 			<sub-item :chil="val.children" />
 			<sub-item :chil="val.children" />
 		</el-sub-menu>
 		</el-sub-menu>
 		<template v-else>
 		<template v-else>
-			<el-menu-item :index="val.path" :key="val.path">
+			<el-menu-item :index="val.path" :key="val.path" v-if="!val.meta.isHide">
 				<template v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
 				<template v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
 					<!-- <SvgIcon :name="val.meta.icon" /> -->
 					<!-- <SvgIcon :name="val.meta.icon" /> -->
 					<span>{{ other.setMenuI18n(val) }}</span>
 					<span>{{ other.setMenuI18n(val) }}</span>

+ 1 - 1
src/layout/routerView/parent.vue

@@ -49,7 +49,7 @@ const menuLeftList = computed(() => {
 	const parentRoute = routeStores.routesList.filter((item: RouteItem) => item.path.includes(parentName));
 	const parentRoute = routeStores.routesList.filter((item: RouteItem) => item.path.includes(parentName));
 	return parentRoute[0].children || [];
 	return parentRoute[0].children || [];
 });
 });
-console.log(other.isMobile(), '移动');
+console.log(menuLeftList.value, '移动');
 const isShowLeftMenu = computed(() => {
 const isShowLeftMenu = computed(() => {
 	return !other.isMobile() && menuLeftList.value.length > 0;
 	return !other.isMobile() && menuLeftList.value.length > 0;
 });
 });

+ 4 - 11
src/views/count/featureUsage/accessPath/index.vue

@@ -2,7 +2,7 @@
 	<div class="layout-padding">
 	<div class="layout-padding">
 		<div class="!overflow-auto pl-1">
 		<div class="!overflow-auto pl-1">
 			<!-- 顶部控制区域 -->
 			<!-- 顶部控制区域 -->
-			<div class="mb-2 el-card p-2">
+			<div class="mb-2 el-card  p-9">
 				<div class="flex items-center mb-4">
 				<div class="flex items-center mb-4">
 					<Title title="页面访问路径" />
 					<Title title="页面访问路径" />
 				</div>
 				</div>
@@ -22,7 +22,7 @@
 					</div>
 					</div>
 				</div>
 				</div>
 			</div>
 			</div>
-			<div class="el-card p-2">
+			<div class="el-card  p-9">
 				<div class="flex justify-between items-center mb-2">
 				<div class="flex justify-between items-center mb-2">
 					<Title left-line title="访问路径">
 					<Title left-line title="访问路径">
 						<template #default>
 						<template #default>
@@ -103,7 +103,7 @@
 					<div ref="mainChartRef" style="width: 100%; height: 600px"></div>
 					<div ref="mainChartRef" style="width: 100%; height: 600px"></div>
 				</div>
 				</div>
 			</div>
 			</div>
-			<div class="el-card p-2 mt-2">
+			<div class="el-card  p-9 mt-2">
 				<div class="flex justify-between items-center mb-2">
 				<div class="flex justify-between items-center mb-2">
 					<Title left-line title="访问详情">
 					<Title left-line title="访问详情">
 						<template #default>
 						<template #default>
@@ -172,14 +172,7 @@
 				</div>
 				</div>
 				<!-- 明细表格 -->
 				<!-- 明细表格 -->
 				<div class="mt-3">
 				<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="showDetail1 = !showDetail1">
-							{{ showDetail1 ? '收起明细数据' : '展开明细数据' }} <el-icon class="ml-2"><ArrowDown v-if="showDetail1" /> <ArrowUp v-else /> </el-icon>
-						</div>
-						<div>
-							<el-button>导出</el-button>
-						</div>
-					</div>
+				
 					<el-table v-if="showDetail1" :data="pagedTableRows" border>
 					<el-table v-if="showDetail1" :data="pagedTableRows" border>
 						<el-table-column prop="date" label="页面(Activity/Fragment)" align="center" min-width="140" />
 						<el-table-column prop="date" label="页面(Activity/Fragment)" align="center" min-width="140" />
 						<el-table-column prop="hyyh" label="描述" align="center" min-width="140" />
 						<el-table-column prop="hyyh" label="描述" align="center" min-width="140" />

+ 188 - 0
src/views/count/featureUsage/event/component/trend.vue

@@ -0,0 +1,188 @@
+<template>
+  <div class="w-full" :class="wrapperClass">
+    <div v-if="isLoading" class="p-4">
+      <el-skeleton animated :rows="3" />
+    </div>
+    <div v-else class="relative">
+      <div
+        v-show="hasData"
+        ref="chartRef"
+        class="w-full"
+        :style="{ height }"
+      />
+      <el-empty v-if="!hasData" :description="emptyText" />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, onBeforeUnmount, shallowRef, watch, ref, nextTick } from 'vue'
+import { useResizeObserver } from '@vueuse/core'
+import { ElMessage } from 'element-plus'
+
+// ECharts (tree-shakeable imports)
+import * as echarts from 'echarts/core'
+import type { EChartsCoreOption } from 'echarts'
+import { LineChart } from 'echarts/charts'
+import {
+  GridComponent,
+  TooltipComponent,
+  LegendComponent,
+  DatasetComponent,
+} from 'echarts/components'
+import { CanvasRenderer } from 'echarts/renderers'
+
+echarts.use([LineChart, GridComponent, TooltipComponent, LegendComponent, DatasetComponent, CanvasRenderer])
+
+interface TrendPoint {
+  date: string | number
+  value: number
+}
+
+interface Props {
+  modelValue?: TrendPoint[]
+  data?: TrendPoint[]
+  title?: string
+  height?: string
+  seriesName?: string
+  smooth?: boolean
+  area?: boolean
+  isLoading?: boolean
+  emptyText?: string
+  yMax?: number | null
+  wrapperClass?: string
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: () => [],
+  data: () => [],
+  title: '',
+  height: '260px',
+  seriesName: '消息数量',
+  smooth: true,
+  area: false,
+  isLoading: false,
+  emptyText: '暂无数据',
+  yMax: null,
+  wrapperClass: '',
+})
+
+const chartRef = ref<HTMLDivElement | null>(null)
+const chartInstance = shallowRef<echarts.ECharts | null>(null)
+
+const mergedData = computed<TrendPoint[]>(() => {
+  return props.data && props.data.length ? props.data : props.modelValue
+})
+
+const hasData = computed(() => mergedData.value && mergedData.value.length > 0)
+const height = computed(() => props.height)
+const wrapperClass = computed(() => props.wrapperClass)
+const emptyText = computed(() => props.emptyText)
+const isLoading = computed(() => props.isLoading)
+
+function buildOption(points: TrendPoint[]): EChartsCoreOption {
+  const x = points.map((p) => p.date)
+  const y = points.map((p) => p.value)
+
+  return {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: { type: 'line' },
+    },
+    legend: { data: [props.seriesName] },
+    grid: { left: 36, right: 20, top: 24, bottom: 36, containLabel: true },
+    xAxis: {
+      type: 'category',
+      boundaryGap: false,
+      data: x,
+      axisLine: { lineStyle: { color: '#e5e7eb' } },
+      axisLabel: { color: '#6b7280' },
+    },
+    yAxis: {
+      type: 'value',
+      max: props.yMax === null ? undefined : props.yMax,
+      axisLine: { show: false },
+      splitLine: { lineStyle: { color: '#f1f5f9' } },
+      axisLabel: { color: '#6b7280' },
+    },
+    series: [
+      {
+        type: 'line',
+        name: props.seriesName,
+        data: y,
+        smooth: props.smooth,
+        showSymbol: true,
+        symbolSize: 6,
+        lineStyle: { width: 2, color: '#3b82f6' },
+        itemStyle: { color: '#3b82f6' },
+        areaStyle: props.area
+          ? {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                { offset: 0, color: 'rgba(59,130,246,0.25)' },
+                { offset: 1, color: 'rgba(59,130,246,0.03)' },
+              ]),
+            }
+          : undefined,
+      },
+    ],
+  }
+}
+
+function initChart() {
+  if (!chartRef.value) return
+  // Dispose if exists
+  if (chartInstance.value) {
+    chartInstance.value.dispose()
+    chartInstance.value = null
+  }
+  try {
+    chartInstance.value = echarts.init(chartRef.value)
+    if (hasData.value) {
+      chartInstance.value.setOption(buildOption(mergedData.value))
+    }
+  } catch (error) {
+    ElMessage.error('图表初始化失败')
+  }
+}
+
+function render() {
+  if (!chartInstance.value || !hasData.value) return
+  chartInstance.value.setOption(buildOption(mergedData.value), true)
+}
+
+onMounted(async () => {
+  await nextTick()
+  initChart()
+})
+
+onBeforeUnmount(() => {
+  if (chartInstance.value) {
+    chartInstance.value.dispose()
+    chartInstance.value = null
+  }
+})
+
+// Resize handling
+useResizeObserver(chartRef, () => {
+  if (chartInstance.value) {
+    chartInstance.value.resize()
+  }
+})
+
+watch(
+  () => [mergedData.value, props.smooth, props.area, props.yMax, props.seriesName],
+  () => {
+    if (!chartInstance.value) initChart()
+    render()
+  },
+  { deep: true }
+)
+</script>
+
+<style scoped lang="scss">
+.relative {
+  position: relative;
+}
+</style>
+
+

+ 298 - 0
src/views/count/featureUsage/event/eventData.vue

@@ -0,0 +1,298 @@
+<template>
+	<div class="layout-padding">
+		<div class="!overflow-auto pl-1">
+			<!-- 顶部控制区域 -->
+			<div class="mb-2 el-card p-9">
+				<div class="flex items-center mb-4">
+					<Title title="自定义事件" />
+				</div>
+
+				<div class="flex items-center justify-between space-x-4">
+					<div class="flex items-center">
+						<el-select v-model="formData.selectedChannelCompare" class="!w-[180px]" placeholder="版本选择">
+							<el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+					</div>
+					<div class="flex items-center ml-2">
+						<el-select v-model="formData.selectedChannelCompare" class="!w-[180px] ml-2" placeholder="全部版本">
+							<el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<el-select v-model="formData.selectedChannelCompare" class="!w-[180px] ml-2" placeholder="全部渠道">
+							<el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<el-date-picker v-model="formData.time" class="w-[200px] ml-2" type="daterange" start-placeholder="开始时间" end-placeholder="结束时间" />
+					</div>
+				</div>
+			</div>
+			<div class="el-card p-9 mt-2">
+				<div class="flex justify-between items-center mb-2">
+					<Title left-line title="事件统计">
+						<template #default>
+							<el-popover class="box-item" placement="right" trigger="hover" width="600">
+								<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>
+											<p><span class="highlight">持续时长:</span><span>事件持续的时间长度</span></p>
+										</div>
+									</div>
+								</template>
+							</el-popover>
+						</template>
+					</Title>
+				</div>
+				<div class="flex justify-between items-center mb-2">
+					<div class="flex items-center">
+						<el-select v-model="formData.selectedChannelCompare" class="!w-[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">行业均值</el-button>
+					</div>
+					<el-radio-group v-model="timeGranularity">
+						<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="xxsc">消息时长</el-radio-button>
+					</el-radio-group>
+				</div>
+				<div class="relative">
+					<div ref="lineChartRef" style="width: 100%; height: 320px"></div>
+				</div>
+			</div>
+			<div class="el-card p-9 mt-2">
+				<!-- 明细表格 -->
+				<Title left-line title="事件统计明细" />
+				<div class="mt-4">
+					<el-table :data="pagedTableRows" border>
+						<el-table-column prop="date" label="日期" align="center" min-width="140" />
+						<el-table-column prop="hyyh" label="消息数量" ali gn="center" min-width="140" />
+						<el-table-column prop="hyyh" label="消息数/启动次数" align="center" sortable min-width="140" />
+						<el-table-column prop="hyyh" label="独立用户数" align="center" sortable min-width="140" />
+						<el-table-column prop="hyyh" label="消息时长" align="center" sortable min-width="140" />
+					</el-table>
+					<div class="flex justify-end mt-2">
+						<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>
+				<!-- 趋势图表区域 -->
+				<div v-if="showTrendChart" class="mt-4">
+					<div class="flex justify-between items-center mb-4">
+						<h3 class="text-lg font-medium">趋势分析</h3>
+						<el-button type="primary" link @click="showTrendChart = false">
+							<el-icon><Close /></el-icon>
+							关闭
+						</el-button>
+					</div>
+					<trend :data="trendData" series-name="消息数量" height="320px" :smooth="true" :area="false" :y-max="1200" />
+				</div>
+
+				<!-- 选择模块 -->
+				<div v-if="!showTrendChart" class="flex justify-between items-center mt-4">
+					<div class="flex items-center">
+						<el-select v-model="formData.selectedChannelCompare" class="!w-[180px]" placeholder="版本选择">
+							<el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<el-popover class="box-item" placement="right" trigger="hover" width="600">
+							<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>您可以为事件设置参数,丰富自定义统计的内容,每个事件至多同时传100个参数,每个参数至多有1000个取值。</span></p>
+									</div>
+								</div>
+							</template>
+						</el-popover>
+					</div>
+					<div class="flex items-center">
+						<el-radio-group v-model="timeGranularity">
+							<el-radio-button label="day">次数分布</el-radio-button>
+							<el-radio-button label="week">时长分布</el-radio-button>
+						</el-radio-group>
+						<el-button class="ml-2" type="primary" link>导出</el-button>
+					</div>
+				</div>
+				<div v-if="!showTrendChart" class="mt-2">
+					<el-table :data="pagedTableRows" border>
+						<el-table-column prop="date" label="参数值" align="center" min-width="140" />
+						<el-table-column prop="hyyh" label="消息数量" ali gn="center" min-width="140" />
+						<el-table-column prop="hyyh" label="占比" align="center" sortable min-width="140" />
+						<el-table-column fixed="right" label="趋势" align="center" min-width="140">
+							<template #default>
+								<el-button type="primary" link @click="showTrendChart = true">趋势</el-button>
+							</template>
+						</el-table-column>
+					</el-table>
+					<div class="flex justify-end mt-2">
+						<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>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted, watch, computed, defineAsyncComponent, onBeforeUnmount } from 'vue';
+import * as echarts from 'echarts';
+import { Close } from '@element-plus/icons-vue';
+
+const Title = defineAsyncComponent(() => import('/@/components/Title/index.vue'));
+const Trend = defineAsyncComponent(() => import('./component/trend.vue'));
+interface TableRow {
+	date: string;
+	newUsers: number;
+	ratio: string;
+}
+const formData = ref<Record<string, any>>({
+	selectedChannelCompare: '',
+});
+const channelCompareOptions = [
+	{ label: '全部版本', value: '' },
+	{ label: '1.0', value: '1.0' },
+	{ label: '2.0', value: '2.0' },
+];
+
+// 图表相关
+const timeGranularity = ref<'hour' | 'day' | 'week' | 'month'>('week');
+const lineChartRef = ref<HTMLDivElement | null>(null);
+let chartInstance: echarts.ECharts | null = null;
+
+const lineChartData = ref<Array<{ x: string; value: number }>>([
+	{ x: '2025-07-01', value: 900 },
+	{ x: '2025-07-08', value: 1000 },
+	{ x: '2025-07-15', value: 1100 },
+	{ x: '2025-07-22', value: 1000 },
+	{ x: '2025-07-29', value: 600 },
+	{ x: '2025-08-05', value: 300 },
+	{ x: '2025-08-12', value: 250 },
+	{ x: '2025-08-19', value: 200 },
+	{ x: '2025-08-26', value: 650 },
+	{ x: '2025-09-02', value: 950 },
+	{ x: '2025-09-09', value: 900 },
+	{ x: '2025-09-16', value: 120 },
+]);
+
+function initLineChart(): void {
+	if (!lineChartRef.value) return;
+	if (chartInstance) chartInstance.dispose();
+	chartInstance = echarts.init(lineChartRef.value);
+	const option: echarts.EChartsOption = {
+		tooltip: { trigger: 'axis' },
+		grid: { left: 40, right: 20, top: 20, bottom: 30 },
+		xAxis: {
+			type: 'category',
+			data: lineChartData.value.map((d) => d.x),
+			axisLine: { lineStyle: { color: '#e5e7eb' } },
+			axisLabel: { color: '#6b7280' },
+			axisTick: { alignWithLabel: true },
+		},
+		yAxis: {
+			type: 'value',
+			axisLine: { show: false },
+			splitLine: { lineStyle: { color: '#f3f4f6' } },
+			axisLabel: { color: '#6b7280' },
+		},
+		series: [
+			{
+				name: '新增人数',
+				type: 'line',
+				smooth: true,
+				showSymbol: true,
+				symbolSize: 6,
+				itemStyle: { color: '#409EFF' },
+				lineStyle: { color: '#409EFF' },
+				data: lineChartData.value.map((d) => d.value),
+			},
+		],
+	};
+	chartInstance.setOption(option);
+}
+
+onMounted(() => {
+	initLineChart();
+});
+
+watch(timeGranularity, () => {
+	// 静态页面:仅重新渲染
+	initLineChart();
+});
+
+// 表格相关(静态数据)
+const tableRows = ref<TableRow[]>(
+	Array.from({ length: 42 }).map((_, idx) => ({
+		date: `2025-08-${String(11).padStart(2, '0')}`,
+		newUsers: 727,
+		hyyh: '123' + idx,
+		ratio: '97.45%',
+	}))
+);
+
+const currentPage = ref(1);
+const pageSize = ref(5);
+const pagedTableRows = computed(() => {
+	const startIndex = (currentPage.value - 1) * pageSize.value;
+
+	return tableRows.value.slice(startIndex, startIndex + pageSize.value);
+});
+
+// 趋势图表相关
+const showTrendChart = ref(false);
+const trendData = ref([
+	{ date: '2025-07-30', value: 860 },
+	{ date: '2025-07-30', value: 1100 },
+	{ date: '2025-07-30', value: 1000 },
+	{ date: '2025-07-30', value: 920 },
+	{ date: '2025-07-30', value: 600 },
+	{ date: '2025-07-30', value: 250 },
+	{ date: '2025-07-30', value: 200 },
+	{ date: '2025-07-30', value: 620 },
+	{ date: '2025-07-30', value: 1050 },
+	{ date: '2025-07-30', value: 1000 },
+	{ date: '2025-07-30', value: 70 },
+]);
+</script>
+
+<style lang="scss" scoped>
+.el-card {
+	background: white;
+	border-radius: 8px;
+	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+:deep(.el-tabs__item.is-top.is-active) {
+	color: #167af0;
+	background-color: #e8f2fe;
+}
+
+.el-radio-button__inner {
+	border-radius: 4px;
+}
+
+.el-radio-button:first-child .el-radio-button__inner {
+	border-radius: 4px 0 0 4px;
+}
+
+.el-radio-button:last-child .el-radio-button__inner {
+	border-radius: 0 4px 4px 0;
+}
+</style>

+ 154 - 0
src/views/count/featureUsage/event/index.vue

@@ -0,0 +1,154 @@
+<template>
+	<div class="layout-padding">
+		<div class="!overflow-auto pl-1">
+			<!-- 顶部控制区域 -->
+			<div class="mb-2 el-card p-9">
+				<div class="flex items-center mb-4">
+					<Title title="自定义事件" />
+				</div>
+				<div class="flex items-center justify-between space-x-4">
+					<div class="flex items-center">
+						<el-select v-model="formData.selectedChannelCompare" class="!w-[180px]" placeholder="功能选择">
+							<el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<el-select v-model="formData.selectedChannelCompare" class="!w-[180px] ml-2" placeholder="全部事件">
+							<el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
+						</el-select>
+						<div class="flex items-center ml-2">
+							<el-icon class="!text-blue-500 mr-1"><Opportunity /></el-icon>
+							添加新的自定义事件,请前往<el-button link type="primary">事件管理</el-button>
+						</div>
+					</div>
+				</div>
+			</div>
+			<div class="el-card p-9 mt-2">
+				<div class="flex justify-between items-center mb-2">
+					<Title left-line title="事件列表">
+						<template #default>
+							<el-popover class="box-item" placement="right" trigger="hover" width="600">
+								<template #reference>
+									<el-icon class="ml-1" style="color: #a4b8cf"><QuestionFilled /></el-icon>
+								</template>
+								<template #default>
+									<div class="ant-popover-inner-content">
+										<div>
+											<span
+												class="um-vc-text"
+												title="当自定义事件、事件参数、参数值超出版本配额,超出部分将无法正常收数,请您关注当前版本配额,并按需升级"
+												style="color: rgb(0, 0, 0); font-size: 12px"
+												>当自定义事件、事件参数、参数值超出版本配额,超出部分将无法正常收数,请您关注当前版本配额,并按需升级</span
+											>
+										</div>
+									</div>
+								</template>
+							</el-popover>
+						</template>
+					</Title>
+					<div class="flex items-center">
+						<el-button link class="ml-2" type="primary">导出</el-button>
+					</div>
+				</div>
+				<!-- 明细表格 -->
+				<div class="mt-3">
+					<el-table :data="pagedTableRows" border height="calc(100vh - 414px)">
+						<el-table-column prop="date" label="事件ID" align="center" min-width="140" />
+						<el-table-column prop="hyyh" label="事件名称" align="center" min-width="140" />
+						<el-table-column prop="hyyh" label="昨日消息数" align="center" sortable min-width="140" />
+						<el-table-column prop="hyyh" label="今日消息数" align="center" sortable min-width="140" />
+						<el-table-column prop="hyyh" label="昨日独立用户数" align="center" sortable min-width="140" />
+						<el-table-column prop="ratio" label="跳出率" align="center" min-width="220" />
+						<el-table-column fixed="right" label="详情" align="center" min-width="140">
+							<template #default="scope">
+								<el-button type="primary" link @click="onDetailClick(scope.row)">查看</el-button>
+							</template>
+						</el-table-column>
+					</el-table>
+					<div class="flex justify-end mt-2">
+						<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>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted, watch, computed, defineAsyncComponent, onBeforeUnmount } from 'vue';
+
+const router = useRouter();
+const Title = defineAsyncComponent(() => import('/@/components/Title/index.vue'));
+interface TableRow {
+	date: string;
+	newUsers: number;
+	ratio: string;
+}
+
+const formData = ref<Record<string, any>>({
+	selectedChannelCompare: '',
+});
+
+const channelCompareOptions = [
+	{ label: '全部版本', value: '' },
+	{ label: '1.0', value: '1.0' },
+	{ label: '2.0', value: '2.0' },
+];
+
+// 表格相关(静态数据)
+const tableRows = ref<TableRow[]>(
+	Array.from({ length: 42 }).map((_, idx) => ({
+		date: `2025-08-${String(11).padStart(2, '0')}`,
+		newUsers: 727,
+		hyyh: '123' + idx,
+		ratio: '97.45%',
+		id: idx,
+	}))
+);
+
+const currentPage = ref(1);
+const pageSize = ref(5);
+const pagedTableRows = computed(() => {
+	const startIndex = (currentPage.value - 1) * pageSize.value;
+
+	return tableRows.value.slice(startIndex, startIndex + pageSize.value);
+});
+
+const onDetailClick = (row: any) => {
+	router.push({
+		path: '/count/featureUsage/event/eventData',
+		query: {
+			id: row.id,
+		},
+	});
+};
+</script>
+
+<style lang="scss" scoped>
+.el-card {
+	background: white;
+	border-radius: 8px;
+	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+:deep(.el-tabs__item.is-top.is-active) {
+	color: #167af0;
+	background-color: #e8f2fe;
+}
+
+.el-radio-button__inner {
+	border-radius: 4px;
+}
+
+.el-radio-button:first-child .el-radio-button__inner {
+	border-radius: 4px 0 0 4px;
+}
+
+.el-radio-button:last-child .el-radio-button__inner {
+	border-radius: 0 4px 4px 0;
+}
+</style>

+ 165 - 0
src/views/count/featureUsage/eventConversion/index.vue

@@ -0,0 +1,165 @@
+<template>
+	<div class="layout-padding">
+		<div class="!overflow-auto pl-1">
+			<!-- 顶部控制区域 -->
+			<div class="mb-2 el-card p-9">
+				<div class="flex items-center mb-4">
+					<Title title="转化率报表">
+						<template #default>
+							<el-popover class="box-item" placement="right" trigger="hover" width="600">
+								<template #reference>
+									<el-icon class="ml-1" style="color: #a4b8cf"><QuestionFilled /></el-icon>
+								</template>
+								<template #default>
+									<div class="ant-popover-inner-content">
+										<div style="padding: 16px">
+											<div>
+												<span class="um-vc-text" title="目标事件:" style="color: rgb(33, 150, 243); font-size: 12px">目标事件:</span>
+												<span
+													class="um-vc-text"
+													title="整个事件转化率所关心的最终转化率的目标,是序列中最后一个事件"
+													style="color: rgb(0, 0, 0); font-size: 12px"
+												>
+													整个事件转化率所关心的最终转化率的目标,是序列中最后一个事件
+												</span>
+											</div>
+											<div>
+												<span class="um-vc-text" title="步骤数:" style="color: rgb(33, 150, 243); font-size: 12px">步骤数:</span>
+												<span class="um-vc-text" title="从初始事件到目标事件经历的事件数" style="color: rgb(0, 0, 0); font-size: 12px">
+													从初始事件到目标事件经历的事件数
+												</span>
+											</div>
+											<div>
+												<span class="um-vc-text" title="所选时段转化率:" style="color: rgb(33, 150, 243); font-size: 12px">所选时段转化率:</span>
+												<span class="um-vc-text" title="所选时段内,该事件序列的最终转化率" style="color: rgb(0, 0, 0); font-size: 12px"
+													>所选时段内,该事件序列的最终转化率
+												</span>
+											</div>
+										</div>
+									</div>
+								</template>
+							</el-popover>
+						</template>
+					</Title>
+				</div>
+				<div class="bg-[#f8f8f8] p-4 rounded" style="line-height: 24px">
+					
+					<p class="">
+						<el-icon class="!text-[#167af0] pian-2"><InfoFilled /></el-icon>
+						事件转化率指的是多个自定义事件序列按照指定顺序依次触发的流程中的量化转化模型。通常我们会对应用中的一些关键路径进行分析,来确定整个流程的设计是否合理,各步骤的优劣,是否存在优化的空间等,进而提高最终目标的转化率。
+					事件转化率是基于自定义事件创建的。想要分析关键流程的转化率,需要先集成并<el-button type="primary" link>添加自定义事件</el-button
+					>,并在事件转化率管理页面<el-button type="primary" link>添加转化报表</el-button>。
+					</p>
+				</div>
+				<div class="flex items-center justify-start mt-2 w-[480px]">
+					<el-select v-model="formData.selectedChannelCompare" class="!w-[180px]" placeholder="全部版本">
+						<el-option v-for="item in channelCompareOptions" :key="item.value" :label="item.label" :value="item.value" />
+					</el-select>
+					<el-date-picker v-model="formData.time" class="!w-[200px] ml-2" type="daterange" start-placeholder="开始时间" end-placeholder="结束时间" />
+
+					<div class="flex items-center ml-2">
+						<el-button link type="primary"><el-icon class="!text-blue-500 mr-1"><Tools /></el-icon>设置</el-button>
+					</div>
+				</div>
+			</div>
+			<div class="el-card p-9 mt-2">
+				<!-- 明细表格 -->
+				<div class="">
+					<el-table :data="pagedTableRows" border height="calc(100vh - 468px)">
+						<el-table-column prop="date" label="名称" align="center" min-width="140" />
+						<el-table-column prop="hyyh" label="目标事件" align="center" min-width="140" />
+						<el-table-column prop="hyyh" label="步骤数" align="center" sortable min-width="140" />
+						<el-table-column prop="hyyh" label="所选时段转化率" align="center" sortable min-width="140" />
+						
+					</el-table>
+					<div class="flex justify-end mt-2">
+						<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>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted, watch, computed, defineAsyncComponent, onBeforeUnmount } from 'vue';
+
+const router = useRouter();
+const Title = defineAsyncComponent(() => import('/@/components/Title/index.vue'));
+interface TableRow {
+	date: string;
+	newUsers: number;
+	ratio: string;
+}
+const formData = ref({
+	selectedChannelCompare: '',
+	time:''
+});
+const channelCompareOptions = [
+	{ label: '全部版本', value: '' },
+	{ label: '1.0', value: '1.0' },
+	{ label: '2.0', value: '2.0' },
+];
+
+// 表格相关(静态数据)
+const tableRows = ref<TableRow[]>(
+	Array.from({ length: 42 }).map((_, idx) => ({
+		date: `2025-08-${String(11).padStart(2, '0')}`,
+		newUsers: 727,
+		hyyh: '123' + idx,
+		ratio: '97.45%',
+		id: idx,
+	}))
+);
+
+const currentPage = ref(1);
+const pageSize = ref(5);
+const pagedTableRows = computed(() => {
+	const startIndex = (currentPage.value - 1) * pageSize.value;
+
+	return tableRows.value.slice(startIndex, startIndex + pageSize.value);
+});
+
+const onDetailClick = (row: any) => {
+	router.push({
+		path: '/count/featureUsage/event/eventData',
+		query: {
+			id: row.id,
+		},
+	});
+};
+</script>
+
+<style lang="scss" scoped>
+.pian-2 {
+	transform: translate(0px, 2px);
+}
+.el-card {
+	background: white;
+	border-radius: 8px;
+	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+:deep(.el-tabs__item.is-top.is-active) {
+	color: #167af0;
+	background-color: #e8f2fe;
+}
+
+.el-radio-button__inner {
+	border-radius: 4px;
+}
+
+.el-radio-button:first-child .el-radio-button__inner {
+	border-radius: 4px 0 0 4px;
+}
+
+.el-radio-button:last-child .el-radio-button__inner {
+	border-radius: 0 4px 4px 0;
+}
+</style>

+ 1 - 1
src/views/count/retained/active/index.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
 	<div class="layout-padding">
 	<div class="layout-padding">
 		<div class="!overflow-auto px-1">
 		<div class="!overflow-auto px-1">
-			<div class="el-card p-2">
+			<div class="el-card  p-9">
 				<!-- 顶部控制区域 -->
 				<!-- 顶部控制区域 -->
 				<div class="mb-4">
 				<div class="mb-4">
 					<div class="flex items-center mb-4">
 					<div class="flex items-center mb-4">

+ 1 - 1
src/views/count/retained/freshness/index.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
 	<div class="layout-padding">
 	<div class="layout-padding">
 		<div class="!overflow-auto px-1">
 		<div class="!overflow-auto px-1">
-			<div class="el-card p-2">
+			<div class="el-card  p-9">
 				<!-- 顶部控制区域 -->
 				<!-- 顶部控制区域 -->
 				<div class=" mb-4">
 				<div class=" mb-4">
 					<div class="flex items-center mb-4">
 					<div class="flex items-center mb-4">

+ 2 - 2
src/views/count/retained/user/index.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
 	<div class="layout-padding">
 	<div class="layout-padding">
 		<div class="!overflow-auto px-1">
 		<div class="!overflow-auto px-1">
-			<div class="el-card p-2">
+			<div class="el-card  p-9">
 				<div class="flex justify-between">
 				<div class="flex justify-between">
 					<Title :title="t('retainedUser.analytics')">
 					<Title :title="t('retainedUser.analytics')">
 						<template #default>
 						<template #default>
@@ -47,7 +47,7 @@
 					</el-row>
 					</el-row>
 				</div>
 				</div>
 			</div>
 			</div>
-			<div class="mt-2 el-card p-2">
+			<div class="mt-2 el-card  p-9">
 				<div class="border-b pb-4">
 				<div class="border-b pb-4">
 					<div class="flex items-center justify-between mb-2 mt-3">
 					<div class="flex items-center justify-between mb-2 mt-3">
 						<div>
 						<div>

+ 2 - 2
src/views/count/user/activations/index.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
 	<div class="layout-padding">
 	<div class="layout-padding">
 		<div class="!overflow-auto px-1">
 		<div class="!overflow-auto px-1">
-			<div class="el-card p-2">
+			<div class="el-card  p-9">
 				<div class="flex justify-between">
 				<div class="flex justify-between">
 					<Title :title="t('activations.analytics')">
 					<Title :title="t('activations.analytics')">
 						<template #default>
 						<template #default>
@@ -51,7 +51,7 @@
 					</el-row>
 					</el-row>
 				</div>
 				</div>
 			</div>
 			</div>
-			<div class="mt-2 el-card p-2">
+			<div class="mt-2 el-card  p-9">
 				<div class="">
 				<div class="">
 					<div class="flex items-center justify-between mb-2 mt-3">
 					<div class="flex items-center justify-between mb-2 mt-3">
 						<div>
 						<div>

+ 2 - 2
src/views/count/user/activeUser/index.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
 	<div class="layout-padding">
 	<div class="layout-padding">
 		<div class="!overflow-auto px-1">
 		<div class="!overflow-auto px-1">
-			<div class="el-card p-2">
+			<div class="el-card  p-9">
 				<div class="flex justify-between">
 				<div class="flex justify-between">
 					<Title :title="t('activeUser.analytics')" />
 					<Title :title="t('activeUser.analytics')" />
 					<div class="">
 					<div class="">
@@ -29,7 +29,7 @@
 					</el-row>
 					</el-row>
 				</div>
 				</div>
 			</div>
 			</div>
-			<div class="mt-2 el-card p-2">
+			<div class="mt-2 el-card  p-9">
 				<!-- 新增趋势 -->
 				<!-- 新增趋势 -->
 				<div class="">
 				<div class="">
 					<Title left-line :title="t('activeUser.addtrend')">
 					<Title left-line :title="t('activeUser.addtrend')">

+ 3 - 3
src/views/count/user/adduser/index.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
 	<div class="layout-padding">
 	<div class="layout-padding">
 		<div class="!overflow-auto px-1">
 		<div class="!overflow-auto px-1">
-			<div class="el-card p-2">
+			<div class="el-card  p-9">
 				<div class="flex justify-between">
 				<div class="flex justify-between">
 					<Title :title="t('addUser.analytics')" />
 					<Title :title="t('addUser.analytics')" />
 					<div class="">
 					<div class="">
@@ -22,7 +22,7 @@
 					</el-row>
 					</el-row>
 				</div>
 				</div>
 			</div>
 			</div>
-			<div class="mt-3 el-card p-2">
+			<div class="mt-3 el-card  p-9">
 				<!-- 新增趋势 -->
 				<!-- 新增趋势 -->
 				<div class="">
 				<div class="">
 					<Title left-line :title="t('addUser.addtrend')">
 					<Title left-line :title="t('addUser.addtrend')">
@@ -105,7 +105,7 @@
 					</div>
 					</div>
 				</div>
 				</div>
 			</div>
 			</div>
-			<div class="mt-3 el-card p-2">
+			<div class="mt-3 el-card  p-9">
 				<!-- 新增用户质量 -->
 				<!-- 新增用户质量 -->
 				<div class="">
 				<div class="">
 					<div class="flex items-center justify-between mb-2">
 					<div class="flex items-center justify-between mb-2">

+ 3 - 3
src/views/count/user/versionDistribution/index.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
 	<div class="layout-padding">
 	<div class="layout-padding">
 		<div class="!overflow-auto px-1">
 		<div class="!overflow-auto px-1">
-			<div class="el-card p-2">
+			<div class="el-card  p-9">
 				<div class="flex justify-between">
 				<div class="flex justify-between">
 					<Title :title="t('versionDistribution.analytics')" />
 					<Title :title="t('versionDistribution.analytics')" />
 					<div class="">
 					<div class="">
@@ -23,7 +23,7 @@
 					</el-row>
 					</el-row>
 				</div>
 				</div>
 			</div>
 			</div>
-			<div class="mt-2 el-card p-2">
+			<div class="mt-2 el-card  p-9">
 				<Title left-line :title="selectedChannelCompare === ''? t('versionDistribution.allVersion') : selectedChannelCompare+t('versionDistribution.version')" >
 				<Title left-line :title="selectedChannelCompare === ''? t('versionDistribution.allVersion') : selectedChannelCompare+t('versionDistribution.version')" >
 					<template #default>
 					<template #default>
 						<el-popover class="box-item" placement="right" trigger="hover" width="300">
 						<el-popover class="box-item" placement="right" trigger="hover" width="300">
@@ -125,7 +125,7 @@
 					</div>
 					</div>
 				</div>
 				</div>
 			</div>
 			</div>
-			<div v-if="selectedChannelCompare !== ''" class="mt-2 el-card p-2">
+			<div v-if="selectedChannelCompare !== ''" class="mt-2 el-card  p-9">
 				<div class="flex justify-between">
 				<div class="flex justify-between">
 					<Title left-line :title="'版本用户来源'">
 					<Title left-line :title="'版本用户来源'">
 						<template #default>
 						<template #default>