Commit 88670ed8 by caoyanzhi

update: ai营销-数据统计

parent bfd0324d
......@@ -40,4 +40,14 @@ export const getMemberCount = params => requests(PREFIX + '/get-member-count', p
// 获取提交并执行日志、提交未执行日志
export const getLogs = params => requests('/api-marketing/page-activity-event-exec-log', params, true, false, 'get');
// 获取AI数据统计详情
export const getCustomDetail = params => requests('/api-marketing/statistics/page-data-statistics-custom-detail', params, true, false, 'get');
// 获取AI数据统计活动信息
export const getActivityInfo = params => requests('/api-marketing/statistics/get-activity-info', params, true, false, 'get');
// 获取AI数据统计外呼数据
export const getOutBound = params => requests('/api-marketing/statistics/out-bound', params, true, false, 'get');
// 人群规则回显
export const getMemberCrowd = params => requests('/api-plug/query-member-crowd-new', params, true);
......@@ -3,7 +3,7 @@
<dm-sub-title style="margin-bottom: 5px">客户明细</dm-sub-title>
<div class="page-tip">消费金额字段每天更新 1 次</div>
<el-input v-model="search.search" @change="handleCurrentChange" type="text" placeholder="请输入姓名/昵称/手机号/会员卡号" prefix-icon="el-icon-search" clearable class="search-bar"></el-input>
<el-table :data="tableData" v-loading="loading">
<el-table :data="tableData" v-loading="loading" tooltipEffect="light">
<el-table-column label="基本信息" min-width="200px" show-overflow-tooltip>
<div slot-scope="{ row }" class="member-info">
<img class="member-logo" v-if="row.memberImage" :src="row.memberImage" alt="" />
......
<template>
<div>数据统计</div>
<div class="ai-data-report">
<div class="report-module">
<dm-sub-title>活动信息</dm-sub-title>
<activity-info @flag="getFlag"></activity-info>
</div>
<div class="report-module">
<dm-sub-title> AI外呼数据<span class="title-tip">数据实时更新</span> </dm-sub-title>
<ai-data :ai-data-show="aiDataShow"></ai-data>
</div>
<div class="report-module">
<dm-sub-title> 活动转化数据<span class="title-tip">数据每天更新 1 次</span> </dm-sub-title>
<conversion></conversion>
</div>
</div>
</template>
<script>
// import G2 from '@antv/g2';
import ActivityInfo from './ai-data-report/activity-info.vue';
import AiData from './ai-data-report/ai-data.vue';
import Conversion from './ai-data-report/conversion.vue';
export default {
name: 'AiDataReport',
components: { ActivityInfo, AiData, Conversion },
data() {
return {
aiDataShow: {
analyseFlag: 0,
smsFlag: 0
}
};
},
methods: {
getFlag(flag) {
this.aiDataShow = flag;
}
}
};
</script>
<style lang="scss" scoped>
.ai-data-report {
background-color: #f7f8fa;
.report-module {
padding: 20px;
background-color: #fff;
+ .report-module {
margin-top: 16px;
}
.title-tip {
margin-left: 16px;
font-size: 12px;
font-weight: 400;
color: #606266;
line-height: 17px;
}
}
}
</style>
<template>
<div>
<div class="activity-info">
<div class="activity-info-item">
<span class="activity-info-label"> 活动名称: </span>
{{ activityInfo.activityName || '--' }}
</div>
<div class="activity-info-item">
<span class="activity-info-label">活动状态:</span>
<i
:class="{
'dm-status--info': activityInfo.status == 1 || activityInfo.status == 4,
'dm-status--primary--flash': activityInfo.status == 2,
'dm-status--error': activityInfo.status == 3,
'dm-status--warning': activityInfo.status == 5
}"
></i>
{{ activityInfo.status | formatStatus }}
</div>
<div class="activity-info-item">
<span class="activity-info-label">活动有效期:</span>
{{ formatDateTimeByType(activityInfo.startDate, 'yyyy-MM-dd') || '--' }}
{{ formatDateTimeByType(activityInfo.endDate, 'yyyy-MM-dd') || '--' }}
</div>
<div class="activity-info-item">
<span class="activity-info-label">活动分析天数:</span>
触达之日起 {{ activityInfo.analyseDays || '--' }}
</div>
</div>
<div class="member-rule">
<div class="member-rule-title">人群规则:</div>
<div class="member-rule-list" v-if="activityInfo.memberType == 0">
<div class="member-rule-item">{{ memberRule || '--' }}</div>
</div>
<div class="member-rule-list" v-if="activityInfo.memberType == 1">
<div class="member-rule-item">生日范围:3月1日-3月31日</div>
<div class="member-rule-item">会员等级:银卡、金卡</div>
<div class="member-rule-item">金字塔会员分层:核心会员31-90天、潜力会员31-90天</div>
<div class="member-rule-item">会员服务门店:会员分组(华南直营、华北直营)</div>
</div>
</div>
</div>
</template>
<script>
import { formatDateTimeByType } from '@/utils/index.js';
import { getActivityInfo, getMemberCrowd } from '@/service/api/aiApi.js';
import gicNewMemberGroup from '@/components/dm-new-member-group/index.vue';
export default {
name: 'ActivityInfo',
components: { gicNewMemberGroup },
data() {
return {
activityInfo: {},
memberRule: ''
};
},
filters: {
formatStatus(state) {
const status = {
1: '未开始',
2: '进行中',
3: '已终止',
4: '已结束',
5: '欠费暂停'
};
return status[state] || '--';
}
},
created() {
this.getActivityInfo();
},
methods: {
formatDateTimeByType,
getActivityInfo() {
const activityId = this.$route.params.id;
const planId = this.$route.query.planId;
getActivityInfo({ activityId, planId }).then(res => {
this.activityInfo = res.result;
this.$emit('flag', {
analyseFlag: this.activityInfo.analyseFlag, //1开0关 开启活动分析开关
smsFlag: this.activityInfo.smsFlag //挂机短信1是0否
});
this.getMemberCrowd();
});
},
getMemberCrowd() {
getMemberCrowd({ memberCrowdWidgetId: this.activityInfo.filterJson }).then(res => {
if (res.result) {
this.memberRule = res.result.filterFrontShow;
}
});
}
}
};
</script>
<style lang="scss" scoped>
.activity-info {
display: flex;
justify-content: flex-start;
align-items: center;
margin-top: 16px;
font-size: 14px;
font-weight: 400;
color: #303133;
line-height: 20px;
.activity-info-item {
+ .activity-info-item {
margin-left: 90px;
}
.activity-info-label {
margin-right: 10px;
}
}
}
.member-rule {
margin-top: 20px;
.member-rule-title {
font-size: 14px;
font-weight: 400;
color: #303133;
line-height: 20px;
}
.member-rule-list {
margin-top: 10px;
padding: 16px;
background: #f7f8fa;
border-radius: 4px;
.member-rule-item {
font-size: 14px;
font-weight: 400;
color: #303133;
line-height: 20px;
+ .member-rule-item {
margin-top: 10px;
}
}
}
}
</style>
<template>
<div>
<el-row :gutter="20">
<el-col :span="6" v-for="(el, index) in targetList" :key="index">
<target-group :data-list="el"></target-group>
</el-col>
</el-row>
<el-row class="chart-group">
<el-col :span="8">
<div class="chart-title">意向标签</div>
<div class="chart-box" id="chart-tag"></div>
</el-col>
<el-col :span="8">
<div class="chart-title">话单质量分析</div>
<div class="chart-box" id="chart-record"></div>
</el-col>
<el-col :span="8">
<div class="chart-title">通话时长</div>
<div class="chart-box">
<div id="chart-time"></div>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import { getOutBound } from '@/service/api/aiApi.js';
import TargetGroup from './target-group.vue';
export default {
name: 'AiData',
components: { TargetGroup },
props: {
aiDataShow: {
type: Object,
default: () => ({})
}
},
data() {
return {
activityId: '',
planId: '',
originTargetList: [
[
{
label: '营销人数',
tips: '根据每天外呼人数累加',
value: '',
key: 'marketingNumber'
},
{
label: '已外呼数',
value: '',
key: 'outboundNumber'
}
],
[
{
label: '总接通数',
value: '',
key: 'totalConnectionNumber'
},
{
label: '电话接通率',
tips: '电话接通率:表示任务中接通号码的占比<br/>计算公式为:接通的电话通数/已经外呼的电话通数',
value: '',
key: 'telephoneConnectionRate'
}
],
[
{
label: 'A/B类客户意向',
value: '',
key: 'customerIntentionsNumber'
},
{
label: '接通意向率',
tips: '接通意向率:表示所有已接通的通话中意向客户占比,其中A类和B类客户为意向客户<br/>计算公式为:A/B类客户意向数/已接通的电话数',
value: '',
key: 'connectionIntentionRate'
}
],
[
{
label: '总挂机数',
value: '',
key: 'totalHangUps'
},
{
label: '挂机率',
tips: '挂机率表示接通后5s之内挂断的电话<br/>计算公式为:总挂机数/接通的电话通数',
value: '',
key: 'hangUpRate'
}
],
[
{
label: '短信发送总数',
value: '',
key: 'sentMessagesNumber'
},
{
label: '发送成功数',
value: '',
key: 'sentSuccessfullyNumber'
}
],
[
{
label: '总通话时长',
value: '',
key: 'totalCallDuration'
},
{
label: '平均通话时长',
value: '',
key: 'averageCallDuration'
}
],
[
{
label: '活动费用',
tips: '不包含短信发送失败退回金额',
value: '',
unit: '元',
key: 'activityCost'
}
]
],
targetData: {}
};
},
computed: {
targetList() {
return this.originTargetList
.map(el => {
return el.filter(item => {
// 活动不发送挂机短信时,数据指标不展示短信发送总数的字段
// 活动未开启活动分析时,数据指标不展示活动费用
item.value = this.targetData[item.key] == null ? '--' : this.targetData[item.key];
return !((this.aiDataShow.analyseFlag == 0 && item.key == 'sentMessagesNumber') || (this.aiDataShow.smsFlag == 0 && item.key == 'activityCost'));
});
})
.filter(el => el.length > 0);
}
},
created() {
this.activityId = this.$route.params.id;
this.planId = this.$route.query.planId;
this.getOutBound();
},
methods: {
getOutBound() {
getOutBound({ activityId: this.activityId, planId: this.planId }).then(res => {
res.result = {
marketingNumber: 1,
outboundNumber: 2,
totalConnectionNumber: 3,
telephoneConnectionRate: 4,
customerIntentionsNumber: 5,
connectionIntentionRate: 6,
totalHangUps: 7,
hangUpRate: 8,
sentMessagesNumber: 9,
sentSuccessfullyNumber: 10,
totalCallDuration: 11,
averageCallDuration: 12,
activityCost: 13
};
this.targetData = res.result;
});
}
}
};
</script>
<style lang="scss" scoped>
.chart-group {
width: 100%;
.chart-title {
margin-top: 30px;
font-size: 14px;
font-weight: 600;
color: #303133;
line-height: 20px;
}
.chart-box {
width: 100%;
height: 220px;
}
}
</style>
<template>
<div>
<el-select class="contrast" placeholder="选择对比项" clearable>
<el-option v-for="el in contrast" :key="el.value" :value="el.value" :label="el.label"></el-option>
</el-select>
<div class="chart-box">
<div class="chart-member-count" id="chart-member-count"></div>
<el-row :gutter="20" class="target-list">
<el-col :span="index == 0 ? 24 : 12" v-for="(el, index) in targetList" :key="index">
<target-group :data-list="el"></target-group>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import TargetGroup from './target-group.vue';
export default {
name: 'Conversion',
components: { TargetGroup },
data() {
return {
contrast: [
{ label: '会员类型', value: 'type' },
{ label: '会员等级', value: 'level' },
{ label: '金字塔会员分层', value: 'fixed' },
{ label: '客户分组', value: 'group' }
],
targetList: [
[
{
label: '活动目标(销售额)',
unit: '元',
value: '200,000'
},
{
label: '实际达成(销售额)',
unit: '元',
value: '200,000'
},
{
label: '实际达成率',
value: '200.00%'
}
],
[
{
label: '活动费用',
tips: '不包含短信发送失败退回金额',
unit: '元',
value: '2,276.1'
},
{
label: 'ROI',
value: '1:88'
}
],
[
{
label: '客单价',
unit: '元',
value: '196.39'
},
{
label: '连带率',
value: '1.2'
}
]
]
};
}
};
</script>
<style lang="scss" scoped>
.contrast {
margin-top: 16px;
width: 160px;
}
.chart-box {
display: flex;
justify-content: flex-start;
align-items: center;
.chart-member-count,
.target-list {
flex-shrink: 0;
width: 50%;
min-height: 332px;
}
}
</style>
<template>
<div class="target-group">
<div class="target" :style="{ width: `${100 / dataList.length}%` }" v-for="(el, index) in dataList" :key="index">
<div class="target-label">
{{ el.label }}
<el-tooltip v-if="el.tips" effect="light" placement="top" popper-class="target-tooltip">
<div slot="content" v-html="el.tips" style="max-width: 450px"></div>
<i class="iconfont icon-QuestionCircleOutlined target-label-icon"></i>
</el-tooltip>
</div>
<div class="target-value">
{{ el.value }}<span v-if="el.unit" class="target-value-unit">{{ el.unit }}</span>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TargetGroup',
props: {
dataList: {
type: Array,
default: () => []
}
}
};
</script>
<style lang="scss" scoped>
.target-group {
display: flex;
justify-content: flex-start;
align-items: flex-start;
margin-top: 16px;
padding: 20px;
width: 100%;
min-width: 265px;
background: #f7f8fa;
border-radius: 4px;
box-sizing: border-box;
.target {
flex-shrink: 0;
.target-label {
font-size: 14px;
font-weight: 400;
color: #606266;
line-height: 20px;
.target-label-icon {
font-size: inherit;
}
}
.target-value {
display: flex;
justify-content: flex-start;
align-items: flex-end;
margin-top: 8px;
font-size: 24px;
font-weight: bold;
color: #303133;
line-height: 28px;
.target-value-unit {
font-size: 14px;
line-height: 25px;
}
}
}
}
</style>
<style lang="scss">
.el-tooltip__popper.target-tooltip {
max-width: 475px !important;
}
</style>
......@@ -12,7 +12,7 @@
<div class="temp-title">{{ el.name }}</div>
<div class="temp-info">
<p class="temp-info-text">话术ID:{{ el.aiTemplateId }}</p>
<p class="temp-info-text">更新时间:{{ el.updateTime | formatDateTimeByType }}</p>
<p class="temp-info-text">更新时间:{{ formatDateTimeByType(el.updateTime) || '--' }}</p>
<el-button class="temp-check-btn" type="text" @click="showTempDetail(el)">查看</el-button>
</div>
<div :class="['temp-status', { publish: el.status == -1 || el.status == 0 || el.status == 1 || el.status == 2 || el.status == 3 }, { reject: el.status == 4 }, { published: el.status == 5 }]">
......@@ -62,6 +62,7 @@ export default {
name: 'Message',
data() {
return {
formatDateTimeByType,
tempList: [],
total: 0,
search: {
......@@ -88,7 +89,6 @@ export default {
};
},
filters: {
formatDateTimeByType,
formatStatus(status, statusList) {
const list = [{ label: '三方删除', value: -1 }, ...statusList];
const state = list.find(el => el.value == status);
......
......@@ -183,7 +183,12 @@ export default {
return row.log_flag === 1;
},
handler: row => {
console.log('数据统计');
this.$router.push({
path: '/ai/ai-data-report/' + row.activityId,
query: {
planId: row.planId
}
});
}
}
],
......
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