|
|
@ -37,8 +37,8 @@ |
|
|
end-placeholder="结束时间" |
|
|
end-placeholder="结束时间" |
|
|
size="default" |
|
|
size="default" |
|
|
/> |
|
|
/> |
|
|
<el-button type="primary" class="search-btn">搜索</el-button> |
|
|
|
|
|
<el-button type="primary" class="reset-btn">重置</el-button> |
|
|
|
|
|
|
|
|
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button> |
|
|
|
|
|
<el-button type="primary" class="reset-btn" @click="handleReset">重置</el-button> |
|
|
<el-button type="danger" class="export-btn">数据导出</el-button> |
|
|
<el-button type="danger" class="export-btn">数据导出</el-button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
@ -92,7 +92,7 @@ |
|
|
|
|
|
|
|
|
<!-- 近7天登录趋势 --> |
|
|
<!-- 近7天登录趋势 --> |
|
|
<div class="chart-section"> |
|
|
<div class="chart-section"> |
|
|
<div class="section-title"><el-icon><TrendCharts /></el-icon> 近7天登录趋势</div> |
|
|
|
|
|
|
|
|
<div class="section-title"><el-icon><TrendCharts /></el-icon> {{ chartTrendTitle }}</div> |
|
|
<div ref="chartTrendRef" class="chart-box-large"></div> |
|
|
<div ref="chartTrendRef" class="chart-box-large"></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
@ -223,7 +223,7 @@ |
|
|
import { ref, onMounted, nextTick, watch } from 'vue'; |
|
|
import { ref, onMounted, nextTick, watch } from 'vue'; |
|
|
import { useRoute, useRouter } from 'vue-router'; |
|
|
import { useRoute, useRouter } from 'vue-router'; |
|
|
import * as echarts from 'echarts'; |
|
|
import * as echarts from 'echarts'; |
|
|
import { getUserLoginList } from '../../api/platformData'; |
|
|
|
|
|
|
|
|
import { getUserLoginList, getUserLoginTrend } from '../../api/platformData'; |
|
|
|
|
|
|
|
|
const route = useRoute(); |
|
|
const route = useRoute(); |
|
|
const router = useRouter(); |
|
|
const router = useRouter(); |
|
|
@ -235,6 +235,7 @@ const searchRegion = ref(''); |
|
|
const dateRangeRegion = ref(''); |
|
|
const dateRangeRegion = ref(''); |
|
|
|
|
|
|
|
|
const chartTrendRef = ref(null); |
|
|
const chartTrendRef = ref(null); |
|
|
|
|
|
let chartTrendInstance = null; |
|
|
const chartRegionBarRef = ref(null); |
|
|
const chartRegionBarRef = ref(null); |
|
|
const chartRegionPieRef = ref(null); |
|
|
const chartRegionPieRef = ref(null); |
|
|
const chartRegionMemberPieRef = ref(null); |
|
|
const chartRegionMemberPieRef = ref(null); |
|
|
@ -251,6 +252,8 @@ const loginStats = ref({ |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// 获取增长率的样式类 |
|
|
// 获取增长率的样式类 |
|
|
|
|
|
const chartTrendTitle = ref('近7天登录趋势'); |
|
|
|
|
|
|
|
|
const getGrowthClass = (growthStr) => { |
|
|
const getGrowthClass = (growthStr) => { |
|
|
if (!growthStr) return ''; |
|
|
if (!growthStr) return ''; |
|
|
return growthStr.startsWith('-') ? 'down' : 'up'; |
|
|
return growthStr.startsWith('-') ? 'down' : 'up'; |
|
|
@ -266,6 +269,14 @@ const getGrowthText = (growthStr) => { |
|
|
return `${prefix}${arrow} ${value}`; |
|
|
return `${prefix}${arrow} ${value}`; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 格式化日期 |
|
|
|
|
|
const formatDate = (date) => { |
|
|
|
|
|
if (!date) return ''; |
|
|
|
|
|
const d = new Date(date); |
|
|
|
|
|
const pad = (n) => n < 10 ? '0' + n : n; |
|
|
|
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
const fetchLoginData = async () => { |
|
|
const fetchLoginData = async () => { |
|
|
try { |
|
|
try { |
|
|
const res = await getUserLoginList(); |
|
|
const res = await getUserLoginList(); |
|
|
@ -282,6 +293,83 @@ const fetchLoginData = async () => { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const fetchTrendData = async () => { |
|
|
|
|
|
let params = {}; |
|
|
|
|
|
if (dateRange.value && dateRange.value.length === 2) { |
|
|
|
|
|
params.start_time = formatDate(dateRange.value[0]); |
|
|
|
|
|
params.end_time = formatDate(dateRange.value[1]); |
|
|
|
|
|
chartTrendTitle.value = `${params.start_time} 至 ${params.end_time} 登录趋势`; |
|
|
|
|
|
} else { |
|
|
|
|
|
chartTrendTitle.value = '近7天登录趋势'; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
const res = await getUserLoginTrend(params); |
|
|
|
|
|
console.log("获取用户登录趋势响应:", res); |
|
|
|
|
|
|
|
|
|
|
|
// 兼容处理拦截器 |
|
|
|
|
|
const data = res.list ? res : (res.data && res.data.list ? res.data : null); |
|
|
|
|
|
|
|
|
|
|
|
if (data && data.list) { |
|
|
|
|
|
updateTrendChart(data.list); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
console.error('获取用户登录趋势失败:', e); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const updateTrendChart = (list) => { |
|
|
|
|
|
if (!chartTrendRef.value) return; |
|
|
|
|
|
|
|
|
|
|
|
if (!chartTrendInstance) { |
|
|
|
|
|
chartTrendInstance = echarts.init(chartTrendRef.value); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const dates = list.map(item => item.date); |
|
|
|
|
|
const allUser = list.map(item => item.all_user); |
|
|
|
|
|
const member = list.map(item => item.member); |
|
|
|
|
|
|
|
|
|
|
|
const option = { |
|
|
|
|
|
tooltip: { trigger: 'axis' }, |
|
|
|
|
|
legend: { data: ['所有用户', '会员用户'], top: 'top' }, |
|
|
|
|
|
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, |
|
|
|
|
|
xAxis: { |
|
|
|
|
|
type: 'category', |
|
|
|
|
|
boundaryGap: false, |
|
|
|
|
|
data: dates |
|
|
|
|
|
}, |
|
|
|
|
|
yAxis: { type: 'value' }, |
|
|
|
|
|
series: [ |
|
|
|
|
|
{ |
|
|
|
|
|
name: '所有用户', |
|
|
|
|
|
type: 'line', |
|
|
|
|
|
data: allUser, |
|
|
|
|
|
smooth: true, |
|
|
|
|
|
itemStyle: { color: '#40a9ff' } |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '会员用户', |
|
|
|
|
|
type: 'line', |
|
|
|
|
|
data: member, |
|
|
|
|
|
smooth: true, |
|
|
|
|
|
itemStyle: { color: '#52c41a' } |
|
|
|
|
|
} |
|
|
|
|
|
] |
|
|
|
|
|
}; |
|
|
|
|
|
chartTrendInstance.setOption(option); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleSearch = () => { |
|
|
|
|
|
fetchTrendData(); |
|
|
|
|
|
// 这里也可以加上 fetchLoginData() 如果登录统计也支持搜索参数 |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleReset = () => { |
|
|
|
|
|
dateRange.value = ''; |
|
|
|
|
|
selectedRegion.value = ''; |
|
|
|
|
|
fetchTrendData(); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// Tab 1 数据 |
|
|
// Tab 1 数据 |
|
|
const loginTableData1 = [ |
|
|
const loginTableData1 = [ |
|
|
{ channel: 'App Store', total: '154,832', dailyNew: '', percent: '38%' }, |
|
|
{ channel: 'App Store', total: '154,832', dailyNew: '', percent: '38%' }, |
|
|
@ -333,36 +421,9 @@ const regionColors = ['#68B2FF', '#D94F41', '#69D2AF', '#FFD360', '#ADADAD', '#B |
|
|
const initCharts = () => { |
|
|
const initCharts = () => { |
|
|
nextTick(() => { |
|
|
nextTick(() => { |
|
|
if (activeTab.value === 'loginData') { |
|
|
if (activeTab.value === 'loginData') { |
|
|
if (chartTrendRef.value) { |
|
|
|
|
|
const chart = echarts.init(chartTrendRef.value); |
|
|
|
|
|
chart.setOption({ |
|
|
|
|
|
tooltip: { trigger: 'axis' }, |
|
|
|
|
|
legend: { data: ['新用户', '老用户'], top: 'top' }, |
|
|
|
|
|
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, |
|
|
|
|
|
xAxis: { |
|
|
|
|
|
type: 'category', |
|
|
|
|
|
boundaryGap: false, |
|
|
|
|
|
data: ['六天前', '五天前', '四天前', '三天前', '两天前', '昨天', '今天'] |
|
|
|
|
|
}, |
|
|
|
|
|
yAxis: { type: 'value' }, |
|
|
|
|
|
series: [ |
|
|
|
|
|
{ |
|
|
|
|
|
name: '新用户', |
|
|
|
|
|
type: 'line', |
|
|
|
|
|
data: [1350, 1250, 1100, 1050, 1050, 1300, 1000], |
|
|
|
|
|
smooth: true, |
|
|
|
|
|
itemStyle: { color: '#40a9ff' } |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
name: '老用户', |
|
|
|
|
|
type: 'line', |
|
|
|
|
|
data: [300, 600, 320, 320, 400, 550, 650], |
|
|
|
|
|
smooth: true, |
|
|
|
|
|
itemStyle: { color: '#52c41a' } |
|
|
|
|
|
} |
|
|
|
|
|
] |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 趋势图已经在 fetchTrendData -> updateTrendChart 中初始化和更新 |
|
|
|
|
|
// 这里只需要处理初始无数据时的状态,或者等待 fetchTrendData 调用 |
|
|
|
|
|
fetchTrendData(); |
|
|
} else if (activeTab.value === 'regionalData') { |
|
|
} else if (activeTab.value === 'regionalData') { |
|
|
// 柱状图 |
|
|
// 柱状图 |
|
|
if (chartRegionBarRef.value) { |
|
|
if (chartRegionBarRef.value) { |
|
|
|