<template>
|
<div>
|
<Card class="card fixed-bottom">
|
<affixTime @selected="clickBreadcrumb" />
|
</Card>
|
<Card class="card">
|
<div>
|
<h4>流量概况</h4>
|
|
</div>
|
<div class="box">
|
<div class="box-item">
|
<div>
|
访客数UV
|
</div>
|
<div>
|
{{uvs||0}}
|
</div>
|
</div>
|
|
<div class="box-item">
|
<div>
|
浏览量PV
|
</div>
|
<div>
|
{{pvs||0}}
|
</div>
|
</div>
|
|
</div>
|
|
</Card>
|
|
<!-- <Card class="card">-->
|
<!-- <div>-->
|
<!-- <h4>流量趋势</h4>-->
|
<!-- <div id="orderChart"></div>-->
|
<!-- </div>-->
|
<!-- </Card>-->
|
<Card class="card">
|
<div class="chart-wrapper">
|
<div class="chart-header">
|
<h2>PV/UV 趋势图</h2>
|
</div>
|
<div ref="chartDom" class="chart-container"></div>
|
</div>
|
</Card>
|
|
<Card class="card">
|
<div>
|
<h4>客户增长报表</h4>
|
<Table class="table" stripe :columns="columns" :data="data"></Table>
|
</div>
|
</Card>
|
</div>
|
</template>
|
<script>
|
import affixTime from "@/views/lili-components/affix-time";
|
import * as API_Member from "@/api/member";
|
import { Chart } from "@antv/g2";
|
import * as echarts from 'echarts';
|
import {pvUvData} from "../../api/orderStatistics";
|
import Cookies from "js-cookie";
|
export default {
|
components: { affixTime },
|
|
data() {
|
return {
|
// 时间
|
|
uvs: 0, // 访客数
|
pvs: 0, // 浏览量
|
|
dateList: [
|
// 日期选择列表
|
{
|
title: "今天",
|
selected: false,
|
value: "TODAY",
|
},
|
{
|
title: "昨天",
|
selected: false,
|
value: "YESTERDAY",
|
},
|
{
|
title: "过去7天",
|
selected: true,
|
value: "LAST_SEVEN",
|
},
|
{
|
title: "过去30天",
|
selected: false,
|
value: "LAST_THIRTY",
|
},
|
],
|
|
orderChart: "", // 流量趋势数据
|
params: {
|
searchType: "LAST_SEVEN",
|
year: "",
|
month: "",
|
storeId: JSON.parse(Cookies.get("userInfoSeller")).id || "",
|
},
|
columns: [
|
{
|
key: "date",
|
title: "日期",
|
},
|
{
|
key: "pvNum",
|
title: "浏览量",
|
},
|
{
|
key: "uvNum",
|
title: "访客数",
|
},
|
],
|
|
data: [], // 客户增长报表数据
|
chart:null //新pvuv
|
|
};
|
},
|
watch: {
|
params: {
|
handler(val) {
|
this.uvs = 0;
|
this.pvs = 0;
|
// this.init();
|
},
|
deep: true,
|
},
|
},
|
methods: {
|
getDates(param){
|
const dates = [];
|
const today = new Date();
|
let days = 1;
|
if(param.year !== null && param.year !== undefined && param.month !== null && param.month !== undefined && param.month !=='' ){
|
// 当传入年份和月份时,生成该月份的所有日期
|
const year = parseInt(param.year);
|
const month = parseInt(param.month);
|
// 获取该月的天数
|
const daysInMonth = new Date(year, month, 0).getDate();
|
|
// 生成该月所有日期
|
for (let day = 1; day <= daysInMonth; day++) {
|
const date = new Date(year, month - 1, day); // 月份从0开始,所以要减1
|
dates.push(this.formatDate(date, 'MM-dd'));
|
}
|
|
}else{
|
// 确定天数逻辑不变
|
if (param.searchType === "TODAY") {
|
days = 1;
|
} else if (param.searchType === "YESTERDAY") {
|
days = 1;
|
} else if (param.searchType === "LAST_SEVEN") {
|
days = 7;
|
} else if (param.searchType === "LAST_THIRTY") {
|
days = 30;
|
}
|
for (let i = days - 1; i >= 0; i--) {
|
const date = new Date(today);
|
date.setDate(today.getDate() - i);
|
dates.push(this.formatDate(date, 'MM-dd'));
|
}
|
}
|
// 生成日期数组逻辑不变
|
|
return dates;
|
},
|
// 订单图
|
initChart2(){
|
this.chart = echarts.init(this.$refs.chartDom)
|
this.updateChartData(this.params)//更新 pvuv
|
},
|
initChart() {
|
let uv = [];
|
let pv = [];
|
|
this.data.forEach((item) => {
|
uv.push({
|
date: item.date,
|
uvNum: item.uvNum,
|
title: "访客数UV",
|
pv: item.uvNum,
|
});
|
pv.push({
|
date: item.date,
|
pvNum: item.pvNum,
|
pv: item.pvNum,
|
title: "浏览量PV",
|
});
|
});
|
|
let data = [...uv, ...pv];
|
|
this.orderChart.data(data);
|
this.orderChart.scale({
|
activeQuantity: {
|
range: [0, 1],
|
nice: true,
|
},
|
});
|
this.orderChart.tooltip({
|
showCrosshairs: true,
|
shared: true,
|
});
|
|
this.orderChart
|
.line()
|
.position("date*pv")
|
.color("title")
|
.label("pv")
|
.shape("smooth");
|
|
this.orderChart
|
.point()
|
.position("date*pv")
|
.color("title")
|
.label("pv")
|
.shape("circle")
|
.style({
|
stroke: "#fff",
|
lineWidth: 1,
|
});
|
this.orderChart.area().position("date*pv").color("title").shape("smooth");
|
|
this.orderChart.render();
|
},
|
//------------更新图表数据pvuv start
|
async updateChartData(param) {
|
// 生成日期和PV数据
|
const { pvData ,uvData} = await this.generateChartData(param)
|
// 设置图表配置
|
this.chart.setOption(this.getChartOption(this.getDates(param), pvData, uvData))
|
},
|
// 获取图表配置项
|
// 图表配置项(恢复原始颜色)
|
getChartOption(dates, pvData, uvData) {
|
return {
|
tooltip: {
|
trigger: 'axis',
|
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
borderColor: '#eee',
|
borderWidth: 1,
|
textStyle: { color: '#333' },
|
formatter: (params) => {
|
const date = params[0].name;
|
const pv = params.find(item => item.seriesName === '浏览量(PV)')?.value || 0;
|
const uv = params.find(item => item.seriesName === '独立访客(UV)')?.value || 0;
|
// 优化:添加换行符转义,避免潜在格式问题(可选,不影响功能)
|
return `<div class="font-medium">${date}</div>
|
<div>浏览量(PV): <span style="color: #165DFF; font-weight: bold">${pv.toLocaleString()}</span></div>
|
<div>独立访客(UV): <span style="color: #00b42a; font-weight: bold">${uv.toLocaleString()}</span></div>`;
|
},
|
},
|
legend: {
|
data: ['浏览量(PV)', '独立访客(UV)'],
|
top: 0,
|
textStyle: { color: '#666' }
|
},
|
grid: {
|
left: '3%',
|
right: '4%',
|
bottom: '3%',
|
containLabel: true,
|
top: '15%'
|
},
|
xAxis: {
|
type: 'category',
|
data: dates,
|
axisLine: { lineStyle: { color: '#eee' } },
|
axisTick: { show: false },
|
axisLabel: { interval: 'auto', rotate: 0, color: '#86909C' },
|
splitLine: { show: false }
|
},
|
yAxis: {
|
type: 'value',
|
axisLine: { show: false },
|
axisTick: { show: false },
|
axisLabel: { color: '#86909C', formatter: '{value}' },
|
splitLine: { lineStyle: { color: '#f2f3f5' } },
|
min: 0
|
},
|
series: [
|
// PV折线(保留原始蓝色系,优化点线衔接)
|
{
|
name: '浏览量(PV)',
|
type: 'line',
|
data: pvData,
|
symbol: 'circle',
|
symbolSize: 6, // 保留原有点大小
|
emphasis: {
|
symbolSize: 8,
|
showSymbol: true
|
},
|
lineStyle: {
|
width: 3, // 保留原有线宽
|
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
{ offset: 0, color: '#165DFF' }, // 原始PV主色
|
{ offset: 1, color: '#36CFC9' } // 原始渐变辅助色
|
]),
|
// 关键优化1:线条端点设为圆角,避免“切口”空白
|
cap: 'round',
|
// 关键优化2:线条拐角设为圆角,避免衔接间隙
|
join: 'round',
|
// 辅助:控制拐角圆角大小(与线宽匹配,避免溢出)
|
miterLimit: 3
|
},
|
itemStyle: {
|
color: '#165DFF', // 原始点颜色
|
borderColor: '#fff', // 保留白色边框(增强层次感)
|
// 关键优化3:减小边框宽度,避免遮挡线条导致断层
|
borderWidth: 0 // 从2调整为1.5
|
},
|
areaStyle: {
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{ offset: 0, color: 'rgba(22, 93, 255, 0.2)' }, // 原始填充色
|
{ offset: 1, color: 'rgba(22, 93, 255, 0)' }
|
])
|
},
|
smooth: true,
|
showSymbol: true, // 保留你设置的“默认显示所有点”
|
},
|
// UV折线(保留原始绿色系,同PV优化逻辑)
|
{
|
name: '独立访客(UV)',
|
type: 'line',
|
data: uvData,
|
symbol: 'circle',
|
symbolSize: 6, // 保留原有点大小
|
emphasis: {
|
symbolSize: 8,
|
showSymbol: true
|
},
|
lineStyle: {
|
width: 3, // 保留原有线宽
|
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
{ offset: 0, color: '#00b42a' }, // 原始UV主色
|
{ offset: 1, color: '#72c140' } // 原始渐变辅助色
|
]),
|
// 同PV优化:端点圆角+拐角圆角
|
cap: 'round',
|
join: 'round',
|
miterLimit: 3
|
},
|
itemStyle: {
|
color: '#00b42a', // 原始点颜色
|
borderColor: '#fff', // 保留白色边框
|
// 同PV优化:减小边框宽度
|
borderWidth: 0 // 从2调整为1.5
|
},
|
areaStyle: {
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
{ offset: 0, color: 'rgba(0, 180, 42, 0.2)' }, // 原始填充色
|
{ offset: 1, color: 'rgba(0, 180, 42, 0)' }
|
])
|
},
|
smooth: true,
|
showSymbol: true, // 保留“默认显示所有点”
|
}
|
],
|
animation: true,
|
animationDuration: 1500,
|
animationEasing: 'cubicOut'
|
};
|
},
|
// 生成pv/uv图表数据
|
async generateChartData(param) {
|
// 1. 声明为async函数
|
// 2. 使用await等待接口返回,确保数据获取后再继续
|
let pvData = [];
|
let uvData = [];
|
try {
|
const res = await pvUvData(param); // 等待接口响应
|
if (res.code === 200) {
|
pvData = res.data.pvData;
|
uvData = res.data.uvData;
|
// 计算pv总和(pvs)
|
this.pvs = pvData.reduce((total, current) => {
|
// 确保current是数字(避免接口返回非数字格式)
|
return total + (Number(current) || 0);
|
}, 0);
|
|
// 计算uv总和(uvs)
|
this.uvs = uvData.reduce((total, current) => {
|
return total + (Number(current) || 0);
|
}, 0);
|
const dates = this.getDates(param); // 获取当前筛选条件的日期数组
|
this.data = dates.map((date, index) => {
|
return {
|
date: date, // 日期
|
pvNum: Number(pvData[index]) || 0, // 对应索引的浏览量(容错为0)
|
uvNum: Number(uvData[index]) || 0 // 对应索引的访客数(容错为0)
|
};
|
});
|
}
|
} catch (error) {
|
console.error("接口调用失败", error);
|
// 可根据需求添加错误处理(如默认数据)
|
this.pvs = 0;
|
this.uvs = 0;
|
}
|
|
// 3. 此时pvData和uvData已被接口数据填充,再返回
|
return { pvData, uvData };
|
},
|
formatDate(date) {
|
const month = date.getMonth() + 1; // 月份从0开始,需+1
|
const day = date.getDate();
|
// 补零:如果是个位数,前面加0
|
const formattedMonth = month < 10 ? `0${month}` : month;
|
const formattedDay = day < 10 ? `0${day}` : day;
|
return `${formattedMonth}-${formattedDay}`;
|
},
|
handleResize() {
|
if (this.chart) {
|
this.chart.resize()
|
}
|
},
|
//-------------end
|
// 订单图
|
|
// 时间筛选
|
clickBreadcrumb(item, index) {
|
let callback = JSON.parse(JSON.stringify(item));
|
|
this.params = callback;
|
console.log(this.params)
|
this.updateChartData(this.params)
|
},
|
// 初始化
|
init() {
|
this.orderChart ? this.orderChart.clear() : ''
|
API_Member.getStatisticsList(this.params).then((res) => {
|
if (res.result) {
|
this.data = res.result;
|
res.result.forEach((item) => {
|
this.uvs += parseInt(item.uvNum);
|
this.pvs += parseInt(item.pvNum);
|
});
|
|
if (!this.orderChart) {
|
this.orderChart = new Chart({
|
container: "orderChart",
|
autoFit: true,
|
height: 500,
|
padding: [70, 70, 70, 70],
|
});
|
}
|
this.initChart();
|
}
|
});
|
},
|
},
|
mounted() {
|
// this.init();
|
this.$nextTick(() => {
|
this.initChart2();
|
});
|
window.addEventListener('resize', this.handleResize)
|
},
|
};
|
</script>
|
<style scoped lang="scss">
|
.fixed-bottom{
|
position:sticky;
|
z-index: 999;
|
top: 0;
|
}
|
.table {
|
margin-top: 10px;
|
}
|
.wrapper {
|
padding-bottom: 200px;
|
}
|
.box-item {
|
display: flex;
|
|
flex-direction: column;
|
width: 25%;
|
font-weight: bold;
|
// align-items: center;
|
justify-content: center;
|
> div {
|
margin: 4px;
|
}
|
}
|
.box {
|
background: rgb(250, 250, 250);
|
padding: 10px;
|
margin-top: 10px;
|
display: flex;
|
}
|
.card {
|
margin-bottom: 10px;
|
}
|
.chart-wrapper {
|
width: 100%;
|
height: 500px;
|
background-color: #fff;
|
border-radius: 8px;
|
padding: 20px;
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
margin-bottom: 15px;
|
}
|
.chart-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
}
|
|
.chart-header h2 {
|
font-size: 18px;
|
font-weight: 600;
|
color: #1d2129;
|
}
|
.chart-container {
|
width: 100%;
|
height: 400px;
|
}
|
</style>
|