oil-station/fuintAdmin/src/views/member/analysis.vue
2024-10-22 16:01:59 +08:00

1049 lines
28 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="content">
<div class="tab-box">
<div class="tab_" :class="{active:index== tabindex }" @click="getindex(index)" v-for="(item,index) in tabs"
:key="index">
{{ item.name }}
</div>
</div>
<div v-show="tabindex == 0" style="margin-top: -6px">
<div class="top-box">
<div class="crad-box" id="d1">
<div class="number_">会员人数</div>
<div class="title_">{{ userCount.userNum || 0 }}</div>
</div>
<div class="crad-box" id="d2">
<div class="number_">注销会员数</div>
<div class="title_">{{ userCount.logOffUserNum || 0 }}</div>
</div>
<div class="crad-box" id="d3">
<div class="number_">会员总积分</div>
<div class="title_">{{ userCount.allPoints || 0 }}</div>
</div>
<div class="crad-box" id="d4">
<div class="number_">消耗积分</div>
<div class="title_">{{ userCount.consumePoints || 0 }}</div>
</div>
<div class="crad-box" id="d5">
<div class="number_">会员累计充值</div>
<div class="title_">{{ userCount.accumulateRecharge || 0 }}</div>
</div>
<div class="crad-box" id="d6">
<div class="number_">会员总余额</div>
<div class="title_">{{ userCount.allBalance || 0 }}</div>
</div>
</div>
<div class="conton-box">
<div class="d-s">
<div class="gang"></div>
<div>会员金额统计</div>
</div>
<div style="display: flex;justify-content: space-around">
<div id="ccc" style="width: 600px;height: 300px; "></div>
<div class="bge">
<div class="g-ds" style="box-sizing: border-box;padding: 10px">
<i class="el-icon-warning" style="color: orangered;margin-right: 5px"></i>
计算公式:会员充值+赠送金额-会员消费-注销+退款=会员余额
</div>
<div class="g-ds">
<div class="three-box">
<div class="d-s" style="justify-content: center"> <i class="el-icon-warning" style="color: orangered;margin-right: 5px"></i>充值总额</div>
<div class="t-size">{{ amountCount.rechargeAmount || 0 }}</div>
</div>
<div class="three-box">
<div>充值本金</div>
<div class="t-size">{{ amountCount.rechargePrincipal || 0 }}</div>
</div>
<div class="three-box">
<div>充值赠送</div>
<div class="t-size">{{ amountCount.rechargeGive || 0 }}</div>
</div>
</div>
<div class="g-ds">
<div class="three-box">
<div class="d-s" style="justify-content: center"> <i class="el-icon-warning" style="color: orangered;margin-right: 5px"></i>消费总额</div>
<div class="t-size">{{ amountCount.consumeAmount || 0 }}</div>
</div>
<div class="three-box">
<div>消费本金</div>
<div class="t-size">{{ amountCount.consumePrincipal || 0 }}</div>
</div>
<div class="three-box">
<div>消费赠送</div>
<div class="t-size">{{ amountCount.consumeGive || 0 }}</div>
</div>
</div>
<div class="g-ds">
<div class="three-box">
<div class="d-s" style="justify-content: center"> <i class="el-icon-warning" style="color: orangered;margin-right: 5px"></i>退款总额</div>
<div class="t-size">{{ amountCount.refundAmount || 0 }}</div>
</div>
<div class="three-box">
<div>退款本金</div>
<div class="t-size">{{ amountCount.refundPrincipal || 0 }}</div>
</div>
<div class="three-box">
<div>退款赠送</div>
<div class="t-size">{{ amountCount.refundGive || 0 }}</div>
</div>
</div>
<div class="g-ds">
<div class="three-box">
<div class="d-s" style="justify-content: center"> <i class="el-icon-warning" style="color: orangered;margin-right: 5px"></i>注销总额</div>
<div class="t-size">{{ amountCount.logOffAmount || 0 }}</div>
</div>
<div class="three-box">
<div>注销本金</div>
<div class="t-size">{{ amountCount.logOffPrincipal || 0 }}</div>
</div>
<div class="three-box">
<div>注销赠送</div>
<div class="t-size">{{ amountCount.logOffGive || 0 }}</div>
</div>
</div>
</div>
</div>
</div>
<div class="bottom-box">
<div class="b-box-r">
<div class="d-s">
<div class="gang"></div>
<div>会员发放积分统计</div>
</div>
<div id="bbb" style="width: 750px;height: 300px;"></div>
</div>
<div class="b-box-r">
<div class="d-s">
<div class="gang"></div>
<div>会员消耗积分统计</div>
</div>
<div id="ddd" style="width: 750px;height: 300px;"></div>
</div>
</div>
</div>
<div v-show="tabindex == 1" style="margin-top: -6px">
<div class="bottom-box">
<div class="b-box-r">
<div class="d-s">
<div class="gang"></div>
<div>会员总览</div>
</div>
<div style="display: flex;justify-content: space-between">
<div id="qone" style="width: 150px;height: 160px; " ></div>
<div id="qtwo" style="width: 150px;height: 160px; " ></div>
<div id="qthree" style="width: 150px;height: 160px; " ></div>
<div id="qfour" style="width: 150px;height: 160px; " ></div>
</div>
</div>
<div class="b-box-r">
<div class="d-s">
<div class="gang"></div>
<div>性别分布</div>
</div>
<div style="margin: 20px auto">
<div style="margin-bottom: 10px">女</div>
<el-progress :percentage="sexSpread.femaleProportion" color="#8A9EFF" :stroke-width="widthZ" ></el-progress>
</div>
<div style="margin: 20px auto">
<div style="margin-bottom: 10px">男</div>
<el-progress :percentage="sexSpread.maleProportion" color="#8A9EFF" :stroke-width="widthZ" ></el-progress>
</div>
</div>
</div>
<div class="bottom-box">
<div class="b-box-r">
<div class="d-s">
<div class="gang"></div>
<div>会员储值分布</div>
</div>
<div class="hx">
<div id="rtwo" style="width: 600px;height: 300px; "></div>
</div>
</div>
<div class="b-box-r">
<div class="d-s">
<div class="gang"></div>
<div>会员来源分布</div>
</div>
<div class="hx">
<div id="rthree" style="width: 765px;height: 300px; "></div>
</div>
</div>
</div>
<div class="conton-box" >
<div class="d-s">
<div class="gang"></div>
<div>会员年龄分布</div>
</div>
<div style="display: flex;justify-content: space-between">
<div id="rfour" style="width: 765px;height: 300px; "></div>
<div id="rfive" style="width: 765px;height: 300px; "></div>
</div>
</div>
<div class="bottom-box">
<div class="b-box-r">
<div class="d-s">
<div class="gang"></div>
<div>按年份分段</div>
</div>
<div class="hx">
<div id="rsix" style="width: 765px;height: 300px; "></div>
</div>
</div>
<div class="b-box-r" >
<div class="d-s">
<div class="gang"></div>
<div>按月份分布</div>
</div>
<div class="hx">
<div id="rseven" style="width: 765px;height: 300px; "></div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts';
import {
getAmount, getByMonth, getByYear,
getConsumePoints,
getGrantPoints, getSource, getStoredValue, getUserAge,
getUserNum,
getUserOverview, getUserSex
} from "../../api/staff/user/userTranslate";
export default {
data() {
return {
tabindex: 0,
sees:'momo',
widthZ: 18,
tabs: [
{
name: "会员统计",
},
{
name: "会员画像",
},
],
// 会员人数统计
userCount:{},
// 会员金额统计
amountCount:{},
// 会员发放积分统计
grantPoints:{},
// 会员消耗积分统计
consumePoints:{},
// 会员总览
userOverview:{},
// 性别分布
sexSpread:{},
// 会员储值分布
storedValue:{},
// 来源分布
sourceSpread:{},
// 性别分布
ageSpread:{},
// 年龄分级
yearSpread:{},
// 月份分布
monthSpread:{},
}
},
created() {
this.getUserCount();
},
mounted() {
// this.initChart()
// this.twoChart()
// this.qCharts()
},
methods:{
async getUserPortrait() {
getUserOverview().then(res => {
this.userOverview = res.data;
this.qCharts()
})
getUserSex().then(res => {
this.sexSpread = res.data
this.sexSpread.maleProportion = Math.round(this.sexSpread.maleProportion)
this.sexSpread.femaleProportion = Math.round(this.sexSpread.femaleProportion)
})
await getStoredValue().then(res => {
this.storedValue = res.data;
})
await getSource().then(res => {
this.sourceSpread = res.data
})
await getUserAge().then(res => {
this.ageSpread = res.data;
})
await getByYear().then(res => {
this.yearSpread = res.data;
})
await getByMonth().then(res => {
this.monthSpread = res.data;
})
await this.twoChart()
},
async getUserCount() {
getUserNum().then(res => {
this.userCount = res.data
})
await getAmount().then(res => {
this.amountCount = res.data
})
await getGrantPoints().then(res => {
this.grantPoints = res.data
})
await getConsumePoints().then(res => {
this.consumePoints = res.data
})
await this.initChart()
},
getindex(index) {
this.tabindex = index
if (index == 0){
this.getUserCount();
}else if (index == 1){
this.getUserPortrait();
}
},
countPercentage(upNum,downNum){
let percentage = 0;
if (downNum!=0){
percentage = (upNum / downNum)*100
}
return percentage.toFixed(2)
},
initChart() {
const chart = echarts.init(document.getElementById('ccc'))
const chart1 = echarts.init(document.getElementById('bbb'))
const chart2 = echarts.init(document.getElementById('ddd'))
const option = {
color: [
'#38A0FF',
'#36CBC9',
],
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: '',
type: 'pie',
radius: ['40%', '70%'],
center: ['50%', '60%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'center',
// formatter: '{c|'+this.totalCount+'} \n {d|工单总数}',
formatter: '{d|会员总余额}',
textStyle: {
rich: {
d: {
fontSize: 14,
fontFamily: 'Microsoft YaHei',
fontWeight: '400',
// color: '#7BDBFF',
},
},
},
},
data: [
{ value: this.amountCount.residuePrincipal,
name: '剩余本金:'+this.countPercentage(this.amountCount.residuePrincipal,this.amountCount.residuePrincipal+this.amountCount.residueGive)+'% ¥'+this.amountCount.residuePrincipal },
{ value: this.amountCount.residueGive,
name: '剩余赠送:'+this.countPercentage(this.amountCount.residueGive,this.amountCount.residuePrincipal+this.amountCount.residueGive)+'% ¥'+this.amountCount.residuePrincipal },
]
}
]
}
const option1 = {
color: [
'#FFD500',
'#E1F5FF',
'#8A9EFF',
],
tooltip: {
trigger: 'item'
},
legend: {
left: 'center'
},
series: [
{
name: '',
type: 'pie',
radius: '70%',
data: [
{ value: this.grantPoints.marketingGivePoint, name: '营销赠送' },
{ value: this.grantPoints.upgradationPoint, name: '会员升级赠送' },
{ value: this.grantPoints.consumePoint, name: '消费赠送' },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
const option2 = {
color: [
'#FC6103',
'#A1D576',
'#F8D08C',
'#8CB800',
'#4885B1'
],
tooltip: {
trigger: 'item'
},
legend: {
left: 'center'
},
series: [
{
name: '',
type: 'pie',
radius: '70%',
data: [
{ value: this.consumePoints.commodityExchange, name: '商品兑换' },
{ value: this.consumePoints.lotteryDeduction, name: '抽奖抵扣' },
{ value: this.consumePoints.couponRedemption, name: '优惠券兑换' },
{ value: this.consumePoints.pointReturn, name: '积分退还(退还给商家)' },
{ value: this.consumePoints.userLogOff, name: '会员注销' },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
chart.setOption(option)
chart1.setOption(option1)
chart2.setOption(option2)
},
twoChart() {
const chart = echarts.init(document.getElementById('rtwo'))
const chart1 = echarts.init(document.getElementById('rthree'))
const chart2 = echarts.init(document.getElementById('rfour'))
const chart3 = echarts.init(document.getElementById('rfive'))
const chart4 = echarts.init(document.getElementById('rsix'))
const chart5 = echarts.init(document.getElementById('rseven'))
const option = {
color: [
'#38A0FF',
'#36CBC9',
'#C37FFF'
],
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: '',
type: 'pie',
radius: ['40%', '70%'],
center: ['50%', '60%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'center',
// formatter: '{c|'+this.totalCount+'} \n {d|工单总数}',
formatter: '{d|会员总余额}',
textStyle: {
rich: {
d: {
fontSize: 14,
fontFamily: 'Microsoft YaHei',
fontWeight: '400',
// color: '#7BDBFF',
},
},
},
},
data: [
{ value: this.storedValue.lowTotal, name: '储值金额1000以下' },
{ value: this.storedValue.centreTotal, name: '储值金额1000~3000' },
{ value: this.storedValue.upTotal, name: '储值金额3000以上' },
]
}
]
};
const option1 = {
color: [
'#38A0FF',
],
xAxis: {
type: 'category',
data: ['员工码', '门店扫码', '会员导入', '会员邀请', ]
},
yAxis: {
type: 'value'
},
series: [
{
data: [this.sourceSpread.employeeCode, this.sourceSpread.storeScanCode,
this.sourceSpread.userImport, this.sourceSpread.userInvite, ],
type: 'bar',
barWidth:24,
itemStyle: {
normal: {
color: '#8A9EFF',
barBorderRadius:[30,30,0,0] ,
}
},
}
],
grid: {
left: '3%',
right: '3%',
bottom: '3%',
containLabel: true
}
}
const option2 = {
color: [
'#FC6103',
'#A1D576',
'#F8D08C',
'#8CB800',
'#4885B1'
],
tooltip: {
trigger: 'item'
},
legend: {
left: 'center'
},
series: [
{
name: '',
type: 'pie',
radius: '70%',
data: [
{ value: this.ageSpread.twentyOneTotal, name: '21-30岁' },
{ value: this.ageSpread.thirtyOneTotal, name: '31-40岁' },
{ value: this.ageSpread.fortyOneTotal, name: '41-50岁' },
{ value: this.ageSpread.fiftyOneTotal, name: '51-60岁' },
{ value: this.ageSpread.otherTotal, name: '其他' },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
const option3 = {
color: [
'#38A0FF',
],
xAxis: {
type: 'category',
data: ['0-10岁', '11-20岁', '21-30岁', '31-40岁','41-50岁','51-60岁' ]
},
yAxis: {
type: 'value'
},
series: [
{
data: [this.ageSpread.zeroTotal, this.ageSpread.elevenTotal, this.ageSpread.twentyOneTotal,
this.ageSpread.thirtyOneTotal,this.ageSpread.fortyOneTotal, this.ageSpread.fiftyOneTotal, ],
type: 'bar',
barWidth:24,
itemStyle: {
normal: {
color: '#8A9EFF',
barBorderRadius:[30,30,0,0] ,
}
},
}
],
grid: {
left: '3%',
right: '3%',
bottom: '3%',
containLabel: true
}
}
const option4 = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {
show:false
},
grid: {
left: '0%',
right: '3%',
bottom: '0%',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01]
},
yAxis: {
type: 'category',
data: ['10后', '00后', '90后', '80后', '70后', ],
},
series: [
{
name: '2011',
barWidth:30,
type: 'bar',
color:'#C37FFF',
data: [this.yearSpread.tenAfter, this.yearSpread.zeroAfter, this.yearSpread.ninetyAfter,
this.yearSpread.eightyAfter, this.yearSpread.seventyAfter, ],
itemStyle: {
emphasis: {
barBorderRadius: 7
},
normal: {
barBorderRadius: [0,20,20,0]
}
}
},
],
};
const option5 = {
color: [
'#38A0FF',
],
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月','5月','6月','7月','8月','9月','10月','11月','12月' ]
},
yAxis: {
type: 'value'
},
series: [
{
data: [this.monthSpread.January, this.monthSpread.February, this.monthSpread.March, this.monthSpread.April,
this.monthSpread.May, this.monthSpread.June,this.monthSpread.July,this.monthSpread.August,
this.monthSpread.September,this.monthSpread.October,this.monthSpread.November,this.monthSpread.December, ],
type: 'bar',
barWidth:24,
itemStyle: {
normal: {
color: '#8A9EFF',
barBorderRadius:[30,30,0,0] ,
}
},
}
],
grid: {
left: '3%',
right: '3%',
bottom: '3%',
containLabel: true
}
}
chart.setOption(option)
chart1.setOption(option1)
chart2.setOption(option2)
chart3.setOption(option3)
chart4.setOption(option4)
chart5.setOption(option5)
},
qCharts() {
const chart = echarts.init(document.getElementById('qone'))
const chart1 = echarts.init(document.getElementById('qtwo'))
const chart2 = echarts.init(document.getElementById('qthree'))
const chart3 = echarts.init(document.getElementById('qfour'))
const option = {
color: [
'#81D3F8',
'#EC808D',
'#33B86C',
'#FFD500',
],
tooltip: {
trigger: 'item'
},
legend: {
show:false
},
series: [
{
name: '',
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '60%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'center',
// formatter: '{c|'+this.totalCount+'} \n {d|工单总数}',
formatter: '{d|会员总人数}',
textStyle: {
rich: {
d: {
fontSize: 10,
fontFamily: 'Microsoft YaHei',
fontWeight: '400',
color: '#000',
},
},
},
},
data: this.userOverview.gradeList
// data: [
// { value: 1048, name: '普通会员' },
// { value: 735, name: '黄金会员 ' },
// { value: 1048, name: '白金会员' },
// { value: 735, name: '钻石会员 ' },
// ]
}
]
}
const option1 = {
color: [
'#E9E9E9',
'#FFD500',
],
tooltip: {
trigger: 'item'
},
legend: {
show:false
},
series: [
{
name: '',
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '60%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'center',
// formatter: '{c|'+this.totalCount+'} \n {d|工单总数}',
formatter: '{d|充值总人数}',
textStyle: {
rich: {
d: {
fontSize: 10,
fontFamily: 'Microsoft YaHei',
fontWeight: '400',
color: '#000',
},
},
},
},
data: [
{ value: this.userOverview.rechargeTotal, name: '充值总人数' },
{ value: this.userOverview.userTotal, name: '总数 ' },
]
}
]
}
const option2 = {
color: [
'#E9E9E9',
'#FFD500',
],
tooltip: {
trigger: 'item'
},
legend: {
show:false
},
series: [
{
name: '',
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '60%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'center',
// formatter: '{c|'+this.totalCount+'} \n {d|工单总数}',
formatter: '{d|储值比例}',
textStyle: {
rich: {
d: {
fontSize: 10,
fontFamily: 'Microsoft YaHei',
fontWeight: '400',
color: '#000',
},
},
},
},
data: [
{ value: this.userOverview.storedValue, name: '储值总人数' },
{ value: this.userOverview.userTotal, name: '总数 ' },
]
}
]
}
const option3 = {
color: [
'#E9E9E9',
'#FFD500',
],
tooltip: {
trigger: 'item'
},
legend: {
show:false
},
series: [
{
name: '',
type: 'pie',
radius: ['60%', '80%'],
center: ['50%', '60%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'center',
// formatter: '{c|'+this.totalCount+'} \n {d|工单总数}',
formatter: '{d|总余额(元)}',
textStyle: {
rich: {
d: {
fontSize: 10,
fontFamily: 'Microsoft YaHei',
fontWeight: '400',
color: '#000',
},
},
},
},
data: [
{ value: this.userOverview.rechargeTotal, name: '充值总人数' },
{ value: this.userOverview.userTotal, name: '总数 ' },
]
}
]
}
chart.setOption(option)
chart1.setOption(option1)
chart2.setOption(option2)
chart3.setOption(option3)
}
}
}
</script>
<style lang="scss" scoped>
.content {
background: #f5f7f8;
width: 100%;
}
.tab-box {
width: 100%;
height: 40px;
background: #fff;
display: flex;
box-sizing: border-box;
margin-bottom: 25px;
}
.conton-box{
width: 98%;
border-radius: 10px;
background: #fff;
box-sizing: border-box;
padding: 15px;
margin: 15px auto;
}
.top-box{
width: 98%;
border-radius: 10px;
background: #fff;
display: flex;
align-items: center;
box-sizing: border-box;
padding: 15px;
margin: 15px auto;
}
.d-s{
display: flex;
align-items: center;
}
.gang{
width: 2px;
height: 13px;
background: #FF9655;
border-radius: 1px;
margin-right: 10px;
}
.tab_ {
width: 75px;
height: 100%;
//border-bottom: 2px solid #FF770F;
display: flex;
font-weight: 500;
font-size: 14px;
color: #999999;
align-items: center;
justify-content: center;
margin-left: 50px;
cursor: pointer;
}
.active {
border-bottom: 2px solid #FF770F !important;
color: #FF770F !important;
}
.crad-box{
width: 258px;
height: 80px;
margin-right: 20px;
box-sizing: border-box;
padding: 15px;
}
.number_{
font-size: 14px;
color: #333333;
margin-bottom: 5px;
}
.title_{
font-size: 24px;
font-weight: bold;
color: #333333;
}
#d1{
background: url(../../assets/images/bz1.png) no-repeat;
background-size:100% 100%;
}
#d2{
background: url(../../assets/images/bz2.png) no-repeat;
background-size:100% 100%;
}
#d3{
background: url(../../assets/images/bz3.png) no-repeat;
background-size:100% 100%;
}
#d4{
background: url(../../assets/images/bz4.png) no-repeat;
background-size:100% 100%;
}
#d5{
background: url(../../assets/images/bz5.png) no-repeat;
background-size:100% 100%;
}
#d6{
background: url(../../assets/images/bz6.png) no-repeat;
background-size:100% 100%;
}
.bge{
width: 450px;
border: 1px solid #EEEEEE;
}
.three-box{
width: 150px;
box-sizing: border-box;
padding: 10px;
border-right: 1px solid #EEEEEE;
border-top: 1px solid #EEEEEE;
text-align: center;
}
.g-ds{
display: flex;
align-items: center;
font-weight: 500;
font-size: 14px;
color: #333333;
}
.t-size{
font-weight: bold;
font-size: 14px;
color: #333333;
margin-top: 10px;
}
.bottom-box{
width: 98%;
margin: 15px auto;
display: flex;
justify-content: space-between;
}
.b-box-r{
width: 49%;
border-radius: 10px;
box-sizing: border-box;
padding: 15px;
background: #fff;
width:49.5%;
}
.hx{
display: flex;
justify-content: center;
}
</style>