Commit 15027b9e by caoyanzhi

update: ai营销数据统计

parent 084ced0a
......@@ -13,6 +13,7 @@
<script src="//at.alicdn.com/t/font_3229694_vfjtu9hqyrc.js"></script> <!--GIC3.0营销-->
<link rel="stylesheet" href="//at.alicdn.com/t/font_2996579_875h3lycepk.css"> <!-- 3.0企业 -->
<script src="//at.alicdn.com/t/font_2996579_875h3lycepk.js"></script><!-- 3.0企业 -->
<script src="//at.alicdn.com/t/font_2859043_i7b45sfe90d.js"></script><!--3.0组件库-->
<!-- <link rel="stylesheet" href="//web-1251519181.file.myqcloud.com/components/element.2.12.0.css"> -->
<!-- element 皮肤 -->
<!-- <link rel="stylesheet" type="text/css" href="http://web-1251519181.file.myqcloud.com/lib/elementUI/theme.1.0.1/index.css"> -->
......
......@@ -53,6 +53,13 @@ export const getActivityInfo = params => requests('/api-marketing/statistics/get
// 获取AI数据统计外呼数据
export const getOutBound = params => requests('/api-marketing/statistics/out-bound', params, true, false, 'get');
export const getIntentionLabel = params => requests('/api-marketing/statistics/intention-label', params, true, false, 'get');
export const getBillAnalysis = params => requests('/api-marketing/statistics/bill-quality-analysis', params, true, false, 'get');
export const getCallDuration = params => requests('/api-marketing/statistics/call-duration', params, true, false, 'get');
// 人群规则回显
export const getMemberCrowd = params => requests('/api-plug/query-member-crowd-new', params, true);
export const getGroupByIds = params => requests('/gic-member-tag-web/member-tag-group/queryGroupByIds', params);
// 活动转化数据
export const getPlanStatistics = params => requests('/api-marketing/statistics/plan-statistics', params, true, false, 'get');
......@@ -45,9 +45,10 @@
<script>
import { formatDateTimeByType } from '@/utils/index.js';
import { getActivityInfo, getMemberCrowd } from '@/service/api/aiApi.js';
import { getActivityInfo, getMemberCrowd, getGroupByIds } from '@/service/api/aiApi.js';
import gicNewMemberGroup from '@/components/dm-new-member-group/index.vue';
// TODO 回显人群规则
export default {
name: 'ActivityInfo',
components: { gicNewMemberGroup },
......@@ -87,11 +88,16 @@ export default {
});
},
getMemberCrowd() {
getMemberCrowd({ memberCrowdWidgetId: this.activityInfo.filterJson }).then(res => {
if (res.result) {
const { memberType, filterJson, sceneJson } = this.activityInfo;
if (memberType == 0) {
getMemberCrowd({ memberCrowdWidgetId: filterJson }).then(res => {
this.memberRule = res.result.filterFrontShow;
}
});
});
} else if (memberType == 1) {
getGroupByIds({ memberTagGroupIds: sceneJson }).then(res => {
this.memberRule = res.result.filterFrontShow;
});
}
}
}
};
......
......@@ -8,16 +8,35 @@
<el-row class="chart-group">
<el-col :span="8">
<div class="chart-title">意向标签</div>
<div class="chart-box" id="chart-tag"></div>
<div class="chart-box" id="chart-tag">
<svg aria-hidden="true" class="no-data-icon">
<use xlink:href="#icon-cp-no-data"></use>
</svg>
</div>
</el-col>
<el-col :span="8">
<div class="chart-title">话单质量分析</div>
<div class="chart-box" id="chart-record"></div>
<div class="chart-box" id="chart-record">
<svg aria-hidden="true" class="no-data-icon">
<use xlink:href="#icon-cp-no-data"></use>
</svg>
</div>
</el-col>
<el-col :span="8">
<div class="chart-title">通话时长</div>
<div class="chart-box">
<div id="chart-time"></div>
<div id="chart-duration"></div>
<div v-if="talkTimeData.length > 0">
<div class="talk-legend" v-for="el in talkTimeData" :key="el.color">
<i class="talk-legend-icon" :style="{ background: el.color }"></i>
<span class="talk-legend-label">{{ el.label }}</span>
<span class="talk-legend-count">{{ numFormat(el.count) }}</span>
<span class="talk-legend-percent">{{ el.percent }}</span>
</div>
</div>
<svg v-else aria-hidden="true" class="no-data-icon">
<use xlink:href="#icon-cp-no-data"></use>
</svg>
</div>
</el-col>
</el-row>
......@@ -25,9 +44,12 @@
</template>
<script>
import G2 from '@antv/g2';
import DataSet from '@antv/data-set';
import { numFormat } from '@/utils/index.js';
import { getOutBound } from '@/service/api/aiApi.js';
import { getOutBound, getIntentionLabel, getBillAnalysis, getCallDuration } from '@/service/api/aiApi.js';
import TargetGroup from './target-group.vue';
export default {
name: 'AiData',
components: { TargetGroup },
......@@ -39,6 +61,7 @@ export default {
},
data() {
return {
numFormat,
activityId: '',
planId: '',
// type number:数字、time:时间、amount:金额、rate:百分比
......@@ -142,7 +165,10 @@ export default {
}
]
],
targetData: {}
// 外呼数据
targetData: {},
// 通话时长数据
talkTimeData: []
};
},
computed: {
......@@ -168,7 +194,7 @@ export default {
item.value = typeof value == 'number' ? numFormat(value) : '--';
break;
case 'rate':
item.value = typeof value == 'string' ? value : '--';
item.value = value == null ? '--' : value;
break;
}
// 活动不发送挂机短信时,数据指标不展示短信发送总数的字段
......@@ -183,11 +209,171 @@ export default {
this.activityId = this.$route.params.id;
this.planId = this.$route.query.planId;
this.getOutBound();
this.getIntentionLabel();
this.getBillAnalysis();
this.getCallDuration();
},
methods: {
getOutBound() {
getOutBound({ activityId: this.activityId, planId: this.planId }).then(res => {
this.targetData = res.result;
this.targetData = Array.isArray(res.result) && res.result.length > 0 ? res.result[0] : {};
});
},
getIntentionLabel() {
getIntentionLabel({ activityId: this.activityId, planId: this.planId }).then(res => {
if (!Array.isArray(res.result) || res.result.length == 0) {
return;
}
const originData = res.result[0];
const levels = ['A', 'B', 'C', 'D', 'E', 'F'];
const data = levels.map(el => {
return {
label: `${el}级`,
count: originData['count_' + el],
percent: originData[el + '_proportion']
};
});
const chart = new G2.Chart({
container: 'chart-tag',
forceFit: true,
height: 220,
padding: 10
});
chart.source(data);
chart.coord('theta');
chart.legend(false);
chart
.intervalStack()
.position('count')
.color('percent', ['#2d4dd1', '#239EFF', '#14C9C9', '#05B770', '#F69F3E', '#7D59E0'])
.label('percent', val => {
return {
offset: -40,
textStyle: {
fontSize: 10,
textAlign: 'center',
shadowBlur: 2,
shadowColor: 'rgba(0, 0, 0, .45)',
fill: '#fff'
},
formatter: (text, item) => {
return item.point.label + '\n' + item.point.percent;
}
};
})
.tooltip('label*count*percent', (label, count, percent) => {
return {
title: label,
name: count,
value: percent
};
})
.style({ lineWidth: 1, stroke: '#fff' });
chart.render();
});
},
getBillAnalysis() {
getBillAnalysis({ activityId: this.activityId, planId: this.planId }).then(res => {
if (!Array.isArray(res.result) || res.result.length == 0) {
return;
}
const originData = res.result[0];
const data = [
{ label: '已接通', count: originData['count_3'], percent: originData['3_proportion'] },
{ label: '线路问题', count: originData['count_4'], percent: originData['4_proportion'] },
{ label: '被叫问题', count: originData['count_5'], percent: originData['5_proportion'] }
];
const chart = new G2.Chart({
container: 'chart-record',
forceFit: true,
height: 220,
padding: 10
});
chart.source(data);
chart.coord('theta');
chart.legend(false);
chart
.intervalStack()
.position('count')
.color('percent', ['#2d4dd1', '#239EFF', '#14C9C9'])
.label('percent', () => {
return {
offset: -40,
textStyle: {
fontSize: 10,
textAlign: 'center',
shadowBlur: 2,
shadowColor: 'rgba(0, 0, 0, .45)',
fill: '#fff'
},
formatter: (text, item) => {
return item.point.label + '\n' + item.point.percent;
}
};
})
.tooltip('label*count*percent', (label, count, percent) => {
return {
title: label,
name: count,
value: percent
};
})
.style({ lineWidth: 1, stroke: '#fff' });
chart.render();
});
},
getCallDuration() {
getCallDuration({ activityId: this.activityId, planId: this.planId }).then(res => {
if (!Array.isArray(res.result) || res.result.length == 0) {
return;
}
const originData = res.result[0];
this.talkTimeData = [
{ label: '<10秒', color: '#2d4dd1', count: originData['less_than_10'], percent: originData['10_proportion'] },
{ label: '10-59秒', color: '#D6B38C', count: originData['10_to_59'], percent: originData['10_to_59_proportion'] },
{ label: '60-119秒', color: '#14C9C9', count: originData['60_to_119'], percent: originData['60_to_119_proportion'] },
{ label: '>120秒', color: '#05B770', count: originData['greater_than_120'], percent: originData['120_proportion'] }
];
this.talkTimeData.splice(3, 1);
const data = [
this.talkTimeData.reduce(
(result, el) => {
result[el.label] = el.count;
return result;
},
{ type: '通话时长' }
)
];
const ds = new DataSet();
const dv = ds.createView().source(data);
dv.transform({
type: 'fold',
fields: this.talkTimeData.map(el => el.label), // 展开字段集
key: '通话时长', // key字段
value: '通话数量', // value字段
retains: ['type'] // 保留字段集,默认为除fields以外的所有字段
});
const chart = new G2.Chart({
container: 'chart-duration',
forceFit: true,
height: 84,
padding: 0
});
chart.source(dv);
chart.coord().transpose();
chart.legend(false);
chart.axis(false);
chart
.intervalStack()
.size(24)
.position('type*通话数量')
.color(
'通话时长',
this.talkTimeData.map(el => el.color)
);
chart.render();
});
}
}
......@@ -207,6 +393,43 @@ export default {
.chart-box {
width: 100%;
height: 220px;
text-align: center;
.no-data-icon {
width: 80px;
height: auto;
margin-top: 20px;
}
.talk-legend {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 auto;
max-width: 400px;
font-size: 12px;
font-weight: 400;
color: #606266;
line-height: 17px;
+ .talk-legend {
margin-top: 10px;
}
.talk-legend-icon {
flex-shrink: 0;
margin-right: 10px;
width: 9px;
height: 9px;
border-radius: 9px;
}
.talk-legend-label {
width: 100%;
text-align: left;
}
.talk-legend-count,
.talk-legend-percent {
flex-shrink: 0;
width: 120px;
text-align: right;
}
}
}
}
</style>
......@@ -15,12 +15,16 @@
</template>
<script>
import { getPlanStatistics } from '@/service/api/aiApi.js';
import { numFormat } from '@/utils/index.js';
import TargetGroup from './target-group.vue';
export default {
name: 'Conversion',
components: { TargetGroup },
data() {
return {
activityId: '',
planId: '',
contrast: [
{ label: '会员类型', value: 'type' },
{ label: '会员等级', value: 'level' },
......@@ -32,16 +36,22 @@ export default {
{
label: '活动目标(销售额)',
unit: '元',
value: '200,000'
value: '',
key: 'activityTargetSale',
type: 'amount'
},
{
label: '实际达成(销售额)',
unit: '元',
value: '200,000'
value: '',
key: 'actualSale',
type: 'amount'
},
{
label: '实际达成率',
value: '200.00%'
value: '',
key: 'actualReachRate',
type: 'rate'
}
],
[
......@@ -49,26 +59,63 @@ export default {
label: '活动费用',
tips: '不包含短信发送失败退回金额',
unit: '元',
value: '2,276.1'
value: '',
key: 'activityCost',
type: 'amount'
},
{
label: 'ROI',
value: '1:88'
value: '1:88',
key: 'ROI',
type: 'rate'
}
],
[
{
label: '客单价',
unit: '元',
value: '196.39'
value: '',
key: 'perCustomerTransaction',
type: 'amount'
},
{
label: '连带率',
value: '1.2'
value: '',
key: 'associatedPurchaseRate',
type: 'number'
}
]
]
};
},
created() {
this.activityId = this.$route.params.id;
this.planId = this.$route.query.planId;
this.getPlanStatistics();
},
methods: {
getPlanStatistics() {
getPlanStatistics({ activityId: this.activityId, planId: this.planId }).then(res => {
const targetData = Array.isArray(res.result) && res.result.length > 0 ? res.result[0] : {};
this.targetList = this.targetList.map(el => {
return el.map(item => {
const value = targetData[item.key];
switch (item.type) {
case 'number':
item.value = typeof value == 'number' ? numFormat(value) : '--';
break;
case 'amount':
item.value = typeof value == 'number' ? numFormat(value) : '--';
break;
case 'rate':
item.value = value == null ? '--' : value;
break;
}
return item;
});
});
});
}
}
};
</script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment