Commit 668543ca by liuchenxi

update: 触达效果

parent 3eadff4f
......@@ -11,7 +11,7 @@
</el-breadcrumb>
<h3>
<div>
<span class="content-title">{{ contentTitle }}</span>
<span class="content-title">{{ contentTitle }}<span class="title_tip">每日上午更新昨日数据</span></span>
<div v-html="layoutTips" class="layout--tips--wrap"></div>
</div>
<div v-if="showDescription" class="intro_wrap"><i class="iconfont icon-zhibiaoshuoming introlIcon" alt="" /><span class="intro" @click="() => (drawer = true)">指标说明</span></div>
......@@ -221,5 +221,12 @@ export default {
font-weight: 600;
color: #303133;
line-height: 28px;
display: flex;
align-items: center;
.title_tip {
font-size: 16px;
color: #606266;
margin-left: 10px;
}
}
</style>
......@@ -11,6 +11,9 @@ export default {
},
formatterNumAndFixed() {
return val => parseFloat(val || 0).toLocaleString('zh', { minimumFractionDigits: 2 });
},
timeLimit() {
return this.$route.query.createTime > 1635350400000;
}
}
};
......@@ -67,6 +67,8 @@ export const massDiagram = params => requests(PREFIX + 'ecmMassColumnDiagram', p
export const massList = params => requests(PREFIX + 'ecmMassTaskTable', params); // 单独数据
export const massTable = params => requests(PREFIX + 'ecmMassTaskPage', params); // 下钻表格
export const massTableExport = config.api + PREFIX + 'ecmMassTaskPageExport'; // 导出数据
// 营销类型
export const getTouchType = params => requests(PREFIX + 'ecmTouchTypeForWx', params);
// 智能营销线索页tree
export const getGroupTree = params => requests(PREFIX + 'getGroupTree', params);
......
......@@ -27,7 +27,10 @@
<div class="top">
<span class="txt" v-if="type == '计划整体'">区域详情转化效果</span>
<span v-else></span>
<el-button type="primary" class="btn" @click="cdkeyExport"><i class="iconfont icon-icon_yunxiazai" style="marginRight:6px;fontSize:14px"></i>导出</el-button>
<div>
<el-checkbox label="展开所选区域下门店明细" v-model="expendStoreFlag" @change="changeExpendFlag" class="expend_store"></el-checkbox>
<el-button type="primary" class="btn" @click="cdkeyExport"><i class="iconfont icon-icon_yunxiazai" style="marginRight:6px;fontSize:14px"></i>导出</el-button>
</div>
</div>
<el-table ref="table" tooltipEffect="dark" :data="tableList" element-loading-text="拼命加载中" @sort-change="sortChange" header-cell-class-name="clue-table-header" style="width: 100%">
<el-table-column v-for="(v, i) in tableHeader" :key="i" :prop="v.prop" :min-width="v.minWidth" :label="v.label" :formatter="v.formatter" :sortable="v.sortable" :render-header="onRender" :fixed="v.fixed" show-overflow-tooltip>
......@@ -100,7 +103,8 @@ export default {
projectName: 'marketing', // 当前项目名
exportDialog: false,
excelUrl: '', // 下载数据的地址
params: {} // 导出数据传递的参数
params: {}, // 导出数据传递的参数
expendStoreFlag: false // 导出分组下所有得门店数据
};
},
mounted() {
......@@ -158,16 +162,19 @@ export default {
meth(params).then(res => {
const data = res.result || {};
if (this.type == '计划整体') {
data.customrPrice = data.convSalesAmt * 1 && data.convMbrNum * 1 ? (data.convSalesAmt / data.convMbrNum).toFixed(2) : '--';
data.relatedRate = data.convGoodsCnt * 1 && data.convOrderCnt * 1 ? (data.convGoodsCnt / data.convOrderCnt).toFixed(2) : '--';
data.taskRate = (data.cplTaskTotalCnt / data.taskCnt) * 100;
data.isSales = 1;
data.touchRate = data.touchMbrNum * 1 && data.planMbrNum * 1 ? ((data.touchMbrNum / data.planMbrNum) * 100).toFixed(2) : 0;
data.transformRate = data.convMbrNum * 1 && data.touchMbrNum * 1 ? ((data.convMbrNum / data.touchMbrNum) * 100).toFixed(2) : 0;
this.dynamicParams = [{ ...data }];
} else {
this.dynamicParams = data;
if (this.type == '群发') {
data.massExecuteNum = data.taskExecuteNum;
}
this.dynamicParams = data;
}
});
},
......@@ -215,12 +222,16 @@ export default {
const convMbrText = '成功触达的顾客中,于收益有效期内在区域(门店)消费的人数,按照订单消费门店进行统计。统计“区域合计”数据时,按照每个门店求和计算,即如果一个顾客在同一个区域的两个门店都有消费,在计算区域合计时,会被算作两个人。';
const convOrderText = '智能营销计划触达的顾客中,在收益有效期内在区域(门店)消费产生的订单数。统计区域数据时按照每个门店求和计算。';
const convSalesText = '智能营销计划触达的顾客中,在收益有效期内在区域(门店)消费产生的订单金额。统计区域数据时按照每个门店求和计算。';
const customrPriceText = '触达顾客转化收益/触达顾客转化人数';
const relatedRateText = '触达顾客订单商品件数/触达顾客订单数。在2021年10月28日前创建的智能营销计划不进行连带率统计。';
// eslint-disable-next-line
this.tableHeader = [
...pubilc,
{ label: { name: '触达顾客转化人数', tipText: convMbrText }, prop: 'convMbrNum', align: 'left', sortable: 'custom', formatter: row => this.formatterNum(row.convMbrNum) },
{ label: { name: '触达顾客订单数', tipText: convOrderText }, prop: 'convOrderCnt', align: 'left', sortable: 'custom', formatter: row => this.formatterNum(row.convOrderCnt) },
{ label: { name: '触达顾客转化收益', tipText: convSalesText }, prop: 'convSalesAmt', align: 'left', sortable: 'custom', formatter: row => this.formatterNumAndFixed(row.convSalesAmt) }
{ label: { name: '触达顾客转化收益', tipText: convSalesText }, prop: 'convSalesAmt', align: 'left', sortable: 'custom', formatter: row => this.formatterNumAndFixed(row.convSalesAmt) },
{ label: { name: '客单价', tipText: customrPriceText }, align: 'left', sortable: 'custom', formatter: row => this.formatterNumAndFixed(row.customrPrice) },
{ label: { name: '连带率', tipText: relatedRateText }, align: 'left', sortable: 'custom', formatter: row => (this.timeLimit && row.relatedRate != '--' ? this.formatterNumAndFixed(row.relatedRate) : '--') }
];
}
if (this.type.indexOf('话务') != -1 || this.type == '群发') {
......@@ -292,7 +303,7 @@ export default {
},
getTableList() {
let meth;
let params = { ecmPlanId: this.$route.query.id, pageNum: this.currentPage, pageSize: this.pageSize, orderBy: this.listTabsIndex == 1 ? this.touchOrderBy : this.taskOrderBy, storeGroupId: this.storeGroupId, level: this.level, type: this.listTabsIndex ? 1 : 2 };
let params = { ecmPlanId: this.$route.query.id, pageNum: this.currentPage, pageSize: this.pageSize, orderBy: this.listTabsIndex == 1 ? this.touchOrderBy : this.taskOrderBy, storeGroupId: this.storeGroupId, level: this.level, type: this.listTabsIndex ? 1 : 2, shopShow: this.expendStoreFlag ? 1 : 0 };
if (this.type == '计划整体') {
meth = overAllTable;
} else if (this.type == '话务') {
......@@ -310,6 +321,8 @@ export default {
return;
}
this.tableList = res.result.result.map((el, index) => {
el.customrPrice = el.convSalesAmt / el.convMbrNum;
el.relatedRate = el.convGoodsCnt && el.convOrderCnt ? (el.convGoodsCnt / el.convOrderCnt).toFixed(2) : '--';
if (el.groupName == '区域合计') {
el.index = '';
return el;
......@@ -322,6 +335,9 @@ export default {
});
this.getTableHeader();
},
changeExpendFlag() {
this.getTableList();
},
sortChange(column) {
let prop = column.prop;
let orderby = column.order;
......@@ -381,7 +397,7 @@ export default {
cdkeyExport() {
this.exportDialog = true;
let meth;
let params = { requestProject: 'marketing', ecmPlanId: this.$route.query.id, pageNum: this.currentPage, pageSize: this.pageSize, orderBy: this.listTabsIndex == 1 ? this.touchOrderBy : this.taskOrderBy, storeGroupId: this.storeGroupId, level: this.level, type: this.listTabsIndex ? 1 : 2 };
let params = { requestProject: 'marketing', ecmPlanId: this.$route.query.id, pageNum: this.currentPage, pageSize: this.pageSize, orderBy: this.listTabsIndex == 1 ? this.touchOrderBy : this.taskOrderBy, storeGroupId: this.storeGroupId, level: this.level, type: this.listTabsIndex ? 1 : 2, shopShow: this.expendStoreFlag ? 1 : 0 };
if (this.type == '计划整体') {
meth = overAllTableExport;
} else if (this.type.indexOf('话务') != -1) {
......@@ -506,6 +522,12 @@ export default {
.btn {
font-family: PingFangSC-Regular, PingFang SC;
}
.expend_store {
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 6px 10px 5px 5px;
margin-right: 6px;
}
}
}
}
......
......@@ -47,20 +47,40 @@
<p :class="{ active: isReference && item.isSales == 1 && data[0].transformRate < data[1].transformRate }">转化率</p>
<p :class="{ active: isReference && item.isSales == 1 && data[0].transformRate < data[1].transformRate }">{{ formatterRate(item.transformRate) }}</p>
</div>
<div class="item-bg bg-green center_flex min-w-206">
<div class="item-bg bg-green center_flex min-w-206" :class="[isCluePage && 'h143']">
<div>
<p style="min-width: 130px">{{ item.isSales == 1 ? '触达顾客转化人数' : '转化人数' }}<tip :text="item.isSales == 1 ? experText.convMbrText : controText.convMbrText" /></p>
<p style="min-width: 130px">{{ formatterNum(item.convMbrNum) }}</p>
</div>
</div>
<div class="item-bg bg-green min-w-389">
<div>
<p>{{ item.isSales == 1 ? '触达顾客订单数' : '转化订单数' }}<tip :text="item.isSales == 1 ? experText.convOrderText : controText.convOrderText" /></p>
<p>{{ formatterNum(item.convOrderCnt) }}</p>
</div>
<div>
<p>{{ item.isSales == 1 ? '触达顾客转化收益' : '转化收益' }}<tip :text="item.isSales == 1 ? experText.convSalesText : controText.convSalesText" /></p>
<p>{{ formatterNumAndFixed(item.convSalesAmt) }}</p>
<div class="item-bg bg-green min-w-389 wrap" :class="[isCluePage && 'h143']">
<template v-if="!isCluePage">
<div>
<p>{{ item.isSales == 1 ? '触达顾客订单数' : '转化订单数' }}<tip :text="item.isSales == 1 ? experText.convOrderText : controText.convOrderText" /></p>
<p>{{ formatterNum(item.convOrderCnt) }}</p>
</div>
<div>
<p>{{ item.isSales == 1 ? '触达顾客转化收益' : '转化收益' }}<tip :text="item.isSales == 1 ? experText.convSalesText : controText.convSalesText" /></p>
<p>{{ formatterNumAndFixed(item.convSalesAmt) }}</p>
</div>
</template>
<div class="center_wrap" v-else>
<div>
<p>{{ item.isSales == 1 ? '触达顾客订单数' : '转化订单数' }}<tip :text="item.isSales == 1 ? experText.convOrderText : controText.convOrderText" /></p>
<p>{{ formatterNum(item.convOrderCnt) }}</p>
</div>
<div>
<p>{{ item.isSales == 1 ? '触达顾客转化收益' : '转化收益' }}<tip :text="item.isSales == 1 ? experText.convSalesText : controText.convSalesText" /></p>
<p>{{ formatterNumAndFixed(item.convSalesAmt) }}</p>
</div>
<div>
<p>{{ '客单价(元)' }}<tip :text="controText.customrPriceText" /></p>
<p>{{ formatterNumAndFixed(item.customrPrice) }}</p>
</div>
<div>
<p>{{ '连带率' }}<tip :text="controText.relatedRateText" /></p>
<p>{{ timeLimit && item.relatedRate != '--' ? formatterNumAndFixed(item.relatedRate) : '--' }}</p>
</div>
</div>
</div>
</div>
......@@ -113,7 +133,9 @@ export default {
planMbrText: '当前智能营销计划选择顾客范围中,未进行营销触达的顾客人数。用于与实验组进行对比,评估营销/未营销所带来的转化效果差异。',
convMbrText: '当前智能营销计划选择顾客范围中,未进行营销触达的顾客人数,在收益有效期内产生消费的顾客人数。收益有效期按照实验组首次进行营销触达时间加收益分析天数统计。',
convOrderText: '当前智能营销计划选择顾客范围中,未进行营销触达的顾客,在收益有效期内产生消费的订单数量。收益有效期按照实验组首次进行营销触达时间加收益分析天数统计。',
convSalesText: '当前智能营销计划选择顾客范围里,未进行营销触达的顾客,在收益有效期内产生消费的订单金额。收益有效期按照实验组首次进行营销触达时间加收益分析天数统计。'
convSalesText: '当前智能营销计划选择顾客范围里,未进行营销触达的顾客,在收益有效期内产生消费的订单金额。收益有效期按照实验组首次进行营销触达时间加收益分析天数统计。',
customrPriceText: '触达顾客转化收益/触达顾客转化人数',
relatedRateText: '触达顾客订单商品件数/触达顾客订单数。在2021年10月28日前创建的智能营销计划不进行连带率统计。'
}
};
},
......@@ -259,6 +281,16 @@ export default {
.active {
color: #f5222d !important;
}
.center_wrap {
display: flex;
height: 90%;
flex-wrap: wrap;
flex-direction: column;
justify-content: space-between;
}
.h143 {
height: 143px !important;
}
.min-w-173 {
min-width: 173px;
}
......
......@@ -7,11 +7,11 @@
</div>
</div>
<div v-if="customServiceData">
<h3>{{ $route.query.effectType == 0 ? '客服接口(文本、图文、图片、小程序)' : '客服接口(小程序)' }}</h3>
<h3>客服接口{{ $route.query.effectType == 0 ? `${getBatchType(['text', 'teletext', 'image', 'wxa'])}` : `${getBatchType(['wxa'])}` }}</h3>
<wechat-sum :data="customServiceData" :tipText="customText" />
</div>
<div v-if="batchData" style="margin-top: 30px">
<h3>群发接口(文本、图文、图片)</h3>
<h3>群发接口{{ getBatchType(['text', 'teletext', 'image']) }}</h3>
<wechat-sum :data="batchData" :tipText="batchText" />
</div>
</div>
......@@ -28,6 +28,20 @@ export default {
},
batchData: {
type: Object || undefined
},
touchType: {
type: Object
}
},
computed: {
getBatchType() {
let str = '';
return v => {
v.forEach(el => {
if (this.touchType[el]) str = !str ? this.touchType[el] : str + '、' + this.touchType[el];
});
return str ? `(${str})` : '';
};
}
},
data() {
......
......@@ -3,7 +3,7 @@
<div class="dm-wrap effect">
<div class="title">
<h2>计划整体效果</h2>
<el-link type="primary" :underline="false" style="color: #1890ff" @click="toClue('计划整体')">查看详情<i class="el-icon-arrow-right" style="margin-left: 10px"></i></el-link>
<el-link type="primary" :underline="false" class="detail" @click="toClue('计划整体')"><i class="el-icon-arrow-right"></i>查看详情</el-link>
</div>
<touch-charts :type="0" :createTime="$route.query.createTime" />
<market-list v-if="marketListData.length" :isRepeat="isRepeat" :data="marketListData" :isReference="isReference" :batchNum="batchNum" :batchTimes="batchTime" @toClue="toClue" />
......@@ -12,7 +12,7 @@
<batch-send v-if="findTypeIsExist(1) || findTypeIsExist(2) || findTypeIsExist(3)" :task-list="findTypeObj(optionsList, 1)" :tel-traffic-list="findTypeObj(optionsList, 2)" :msg-list="findTypeObj(optionsList, 3)" @toClue="toClue" />
<phone-traffic v-if="findTypeIsExist(4)" :data="findTypeObj(optionsList, 4)" @toClue="toClue" />
<short-msg v-if="findTypeIsExist(5)" :data="findTypeObj(optionsList, 5)" />
<wechat v-if="findTypeIsExist(6) || findTypeIsExist(7)" :custom-service-data="findTypeObj(optionsList, 6)" :batch-data="findTypeObj(optionsList, 7)" />
<wechat v-if="(findTypeIsExist(6) || findTypeIsExist(7)) && getTouchTypeFlag" :custom-service-data="findTypeObj(optionsList, 6)" :batch-data="findTypeObj(optionsList, 7)" :touch-type="touchType" />
</div>
</template>
<script>
......@@ -23,7 +23,7 @@ import batchSend from '@/views/ecm/touch-components/batch-send/index.vue';
import phoneTraffic from '@/views/ecm/touch-components/phone-traffic/index.vue';
import shortMsg from '@/views/ecm/touch-components/short-msg/index.vue';
import wechat from '@/views/ecm/touch-components/wechat/index.vue';
import { ecmTouchEffectTable, ecmPlanTouchConfig, getCardLeads, getCardLeadsList, ecmTouchTypeTableList } from '@/service/api/ecmApi.js';
import { ecmTouchEffectTable, ecmPlanTouchConfig, getCardLeads, getCardLeadsList, ecmTouchTypeTableList, getTouchType } from '@/service/api/ecmApi.js';
export default {
name: 'ecm',
data() {
......@@ -37,6 +37,8 @@ export default {
batchTime: '', // 批次时间
cardLead: {},
cardLeadTable: [],
touchType: {},
getTouchTypeFlag: false,
optionsList: [] //type 1 群发任务、2 群发失败-话务、3 群发失败-短信、4 话务、5 短信、6 微信-触点 、7 微信-文本、图文、图片
};
},
......@@ -58,6 +60,7 @@ export default {
this.getTouchConfig();
this.getCardLeadsSum();
this.getCardLeadTable();
this.getTouchTypeList();
},
methods: {
toClue(type) {
......@@ -118,6 +121,31 @@ export default {
ecmTouchTypeTableList({ ecmPlanId: this.ecmPlanId }).then(res => {
this.optionsList = res.result || [];
});
},
// 获取营销类型
getTouchTypeList() {
getTouchType({ ecmPlanId: this.$route.params.id })
.then(res => {
this.touchType = {};
const result = Array.isArray(res.result) ? [...new Set(res.result)] : [];
result.forEach(el => {
switch (el) {
case 'text':
this.touchType.text = '文本';
break;
case 'teltext':
this.touchType.teltext = '图文';
break;
case 'wxa':
this.touchType.wxa = '小程序';
break;
case 'image':
this.touchType.iamge = '图片';
break;
}
});
})
.finally(() => (this.getTouchTypeFlag = true));
}
},
computed: {
......@@ -152,5 +180,16 @@ export default {
line-height: 22px;
}
}
.detail {
padding: 6px 10px;
background: rgba(24, 144, 255, 0.1);
border-radius: 4px;
line-height: 20px;
color: #1890ff;
box-sizing: border-box;
i {
margin-right: 2px;
}
}
}
</style>
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