Commit 975b6ea6 by 黑潮

update: 触达效果

parent 3b92a590
......@@ -3,38 +3,38 @@
<div class="middle">
<div class="item-bg bg-purper center_flex min-w-173">
<div>
<p>触达人数<tip :text="tipText.touchMbrNum" /></p>
<p>{{ formatterNum(list.touchMbrNum) }}</p>
<p>计划触达人数<tip :text="tipText.touchMbrNum" /></p>
<p>{{ formatterNum(list.planMbrNum) }}</p>
</div>
</div>
<div class="item-arrow purper center_flex">
<p>领取</p>
<p>{{ formatterRate((list.getMbrNum / list.touchMbrNum) * 100) }}</p>
<p>触达</p>
<p>{{ formatterRate((list.touchMbrNum / list.planMbrNum) * 100) }}</p>
</div>
<div class="item-bg bg-purper center_flex min-w-173">
<div>
<p>领取人数<tip :text="tipText.getMbrNum" /></p>
<p>{{ formatterNum(list.getMbrNum) }}</p>
<p>实际触达人数<tip :text="tipText.getMbrNum" /></p>
<p>{{ formatterNum(list.touchMbrNum) }}</p>
</div>
</div>
<div class="item-arrow purper center_flex">
<p>使用</p>
<p>{{ formatterRate((list.useMbrNum / list.getMbrNum) * 100) }}</p>
<p>转化</p>
<p>{{ formatterRate((list.convMbrNum / list.touchMbrNum) * 100) }}</p>
</div>
<div class="item-bg bg-purper center_flex min-w-214">
<div>
<p>使用人数<tip :text="tipText.useMbrNum" /></p>
<p>{{ formatterNum(list.useMbrNum) }}</p>
<p>触达顾客转化人数<tip :text="tipText.useMbrNum" /></p>
<p>{{ formatterNum(list.convMbrNum) }}</p>
</div>
</div>
<div class="item-bg bg-purper min-w-527">
<div>
<p>销售单数 <tip :text="tipText.orderCnt" /></p>
<p>{{ formatterNum(list.orderCnt) }}</p>
<p>触达顾客订单数<tip :text="tipText.orderCnt" /></p>
<p>{{ formatterNum(list.convOrderCnt) }}</p>
</div>
<div>
<p>销售单金额(元)<tip :text="tipText.salesAmt" /></p>
<p>{{ formatterNumAndFixed(list.salesAmt) }}</p>
<p>触达顾客转化收益(元)<tip :text="tipText.salesAmt" /></p>
<p>{{ formatterNumAndFixed(list.convSalesAmt) }}</p>
</div>
</div>
</div>
......@@ -48,18 +48,18 @@ export default {
components: { tip },
props: {
list: {
type: Array,
type: Object,
default: () => {}
}
},
data() {
return {
tipText: {
touchMbrNum: '当前智能营销计划发出优惠券的顾客数,如果对同一个顾客发送了超过1张优惠券,仅计算为1个人。',
getMbrNum: '当前智能营销计划发出优惠券后,领取优惠券的顾客人数。如果同一个顾客领取了超过1张优惠券,仅计算为1个人。',
useMbrNum: '"当前智能营销计划发出优惠券后,使用优惠券的顾客人数。如果同一个顾客使用了超过1张优惠券,仅计算为1个人。由于是单独从“卡券”维度统计,因此只要是使用了该计划所发送优惠券的都会计入,即使顾客使用该优惠券的订单超过了收益有效期"',
orderCnt: '"当前智能营销计划发出优惠券后,使用优惠券的订单数。如果一个订单使用了超过1张优惠券,仅计算为1个订单。由于是单独从“卡券”维度统计,因此只要是使用了该计划所发送优惠券的订单都会计入,即使该订单超过了收益有效期,也会将该订单计入销售单数。"',
salesAmt: '"当前智能营销计划发出优惠券后,使用优惠券的订单金额。如果一笔订单使用了超过1张优惠券,该金额仅计算一次。由于是单独从“卡券”维度统计,因此只要是使用了该计划所发送优惠券的订单都会计入,即使该订单超过了收益有效期,也会将该订单金额计入。"'
touchMbrNum: '计划下发卡券触达的顾客人数。如果当前计划在不同时间,通过不同区域(门店),用卡券多次触达同一个顾客,会进行去重统计,仅计算为1个人。',
getMbrNum: '实际下发卡券触达的顾客人数。如果当前计划在不同时间,通过不同区域(门店),用卡券多次触达同一个顾客,会进行去重统计,仅计算为1个人。',
useMbrNum: '下发卡券触达的顾客中,在收益有效期内消费的顾客人数。只要下发卡券触达后消费就会被统计,无论卡券有没有被使用。无论顾客消费了几次,会进行去重统计,仅计算为1个人。',
orderCnt: '下发卡券触达的顾客中,在收益有效期内消费的顾客订单数。只要下发卡券触达后消费就会被统计,无论卡券有没有被使用。',
salesAmt: '下发卡券触达的顾客中,在收益有效期内消费的顾客订单金额。只要下发卡券触达后消费就会被统计,无论卡券有没有被使用。'
}
};
},
......
<template>
<div class="dm-wrap card_profit">
<div class="title">
<h2>卡券收益</h2>
<span>计划中的卡券,计划中卡券触达的人群使用该卡券消费的收益,包含已过计划收益有效期的数据</span>
<h2>卡券</h2>
<span>在卡券下发后,下发顾客在营销有效期内的消费转化情况。无论顾客最终有没有使用卡券消费都会统计在内</span>
</div>
<template>
<card-profit-sum :list="list" />
<el-table :data="tableData" v-if="tableData.length > 1" style="margin:20px 0 20px" max-height="710" header-cell-class-name="card-profit-header">
<div class="title" style="margin-top:20px">
<h2 class="subtitle">卡券核销</h2>
<span>卡券核销情况统计通过当前智能营销计划投放的卡券,顾客使用该卡券消费的情况。即使消费时间已经过了收益有效期也会统计在内</span>
</div>
<el-table :data="tableData" v-if="tableData.length > 0" style="margin:20px 0 20px" max-height="710" header-cell-class-name="card-profit-header">
<el-table-column :prop="cardName" label="卡券名称" min-width="150">
<template slot-scope="scope">
<div class="name" v-if="scope.row.cardName">
<div class="top">
<span v-if="scope.row.cardName.length <= 15" :class="[scope.row.status == 0 ? 'del_card' : 'pointer']" @click="toCardDetail(scope.row)">{{ scope.row.cardName }}</span>
<span v-if="scope.row.cardName.length <= 15" :class="[scope.row.status == 0 || !('status' in scope.row) ? 'del_card' : 'pointer']" @click="toCardDetail(scope.row)">{{ scope.row.cardName }}</span>
<el-tooltip open-delay="100" placement="top-start" :content="scope.row.cardName" v-else>
<span :class="['card_name', scope.row.status == 0 ? 'del_card' : 'pointer']" @click="toCardDetail(scope.row)">{{ scope.row.cardName }}</span>
</el-tooltip>
<span class="type" v-if="scope.row.status != 0">{{ scope.row.cardType == 0 ? '抵金券' : scope.row.cardType == 1 ? '折扣券' : scope.row.cardType == 2 ? '兑换券' : '--' }}</span>
<img :src="require('@/assets/img/Icon-yishanchu.svg')" alt="" v-else style="width: 46px;height: 21px;margin-left: 6px" />
<span class="type" v-if="scope.row.status != 0 && 'status' in scope.row">{{ scope.row.cardType == 0 ? '抵金券' : scope.row.cardType == 1 ? '折扣券' : scope.row.cardType == 2 ? '兑换券' : '--' }}</span>
<img :src="require('@/assets/img/Icon-yishanchu.svg')" alt="" v-else-if="'status' in scope.row" style="width: 46px;height: 21px;margin-left: 6px" />
</div>
<p>{{ scope.row.subTitle }}</p>
</div>
......@@ -53,19 +57,18 @@ export default {
data() {
return {
tableHeader: [
{ label: '触达人数', prop: 'touchMbrNum', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.touchMbrNum) },
{ label: '领取人数', prop: 'getMbrNum', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.getMbrNum) },
{ label: '领取率', prop: 'drawRate', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterRate((row.getMbrNum / row.touchMbrNum) * 100) },
{ label: '使用人数', prop: 'useMbrNum', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.useMbrNum) },
{ label: '使用率', prop: 'useRate', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterRate((row.useMbrNum / row.getMbrNum) * 100) },
{ label: '销售单数', prop: 'orderCnt', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.orderCnt) },
{ label: '销售单金额', prop: 'salesAmt', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNumAndFixed(row.salesAmt) }
{ label: '领取人数', prop: 'getMbrNum', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.getMbrNum) },
{ label: '核销率', prop: 'useRate', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterRate((row.useMbrNum / row.getMbrNum) * 100) },
{ label: '核销人数', prop: 'useMbrNum', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.useMbrNum) },
{ label: '核销订单数', prop: 'orderCnt', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNum(row.orderCnt) },
{ label: '核销订单金额', prop: 'salesAmt', minWidth: '160', align: 'left', fixed: 'left', formatter: row => this.formatterNumAndFixed(row.salesAmt) }
]
};
},
methods: {
toCardDetail(row) {
if (row.status == 0) return;
if (row.status == 0 || !('status' in row)) return;
window.open(window.location.origin + `/marketing/#/card/edit/${row.cardId}`);
}
}
......@@ -77,11 +80,24 @@ export default {
padding: 20px !important;
margin-bottom: 10px !important;
font-family: PingFangSC-Regular, PingFang SC;
h2.subtitle {
width: 139px;
height: 25px;
background: #bad9ff;
line-height: 25px;
text-align: center;
border-radius: 2px;
margin-left: -2px;
color: #303133;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 600;
}
.title {
padding-left: 4px;
margin-bottom: 20px;
display: flex;
align-items: center;
// align-items: center;
flex-direction: column;
h2 {
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
......@@ -90,7 +106,8 @@ export default {
line-height: 22px;
}
span {
margin-left: 28px;
// margin-left: 28px;
margin-top: 20px;
font-size: 14px;
color: #909399;
}
......
<template>
<div class="dm-wrap phone-traffic">
<div class="title flex_between">
<div class="flex_between">
<!-- <img :src="require('@/assets/img/icon-phone.png')" class="img" /> -->
<h2>积分</h2>
</div>
</div>
<integral-traffic v-bind="$attrs" />
</div>
</template>
<script>
import integralTraffic from './integral-traffic.vue';
export default {
name: 'integral',
components: { integralTraffic },
inheritAttrs: false
};
</script>
<style lang="scss" scoped>
.phone-traffic {
padding: 22px 20px 30px !important;
font-family: PingFangSC-Regular, PingFang SC;
.title {
height: 32px;
margin-bottom: 10px;
line-height: 32px;
h2 {
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 700;
color: #303133;
margin-left: 2px;
}
}
.img {
transform: scale(0.5);
}
.flex_between {
display: flex;
justify-content: space-between;
align-items: center;
}
.detail {
padding: 0 10px;
background: rgba(24, 144, 255, 0.1);
border-radius: 4px;
height: 32px;
color: #1890ff;
box-sizing: border-box;
display: flex;
align-items: center;
cursor: pointer;
border: none;
&:hover {
color: #46a6ff;
}
i {
margin-right: 2px;
}
}
}
</style>
<template>
<div class="middle">
<div class="item-bg bg-purper center_flex min-w-173">
<div>
<p class="key">计划触达人数<tip :text="tipText.planMbrNum" /></p>
<p class="value">{{ formatterNum(data.planMbrNum) }}</p>
</div>
<div class="item-arrow purper">
<p>触达率</p>
<p>{{ formatterRate((data.touchMbrNum / data.planMbrNum) * 100) }}</p>
</div>
</div>
<div class="item-bg bg-purper center_flex min-w-173">
<div>
<p class="key">实际触达人数<tip :text="tipText.touchMbrNum" /></p>
<p class="value">{{ formatterNum(data.touchMbrNum) }}</p>
</div>
<div class="item-arrow green">
<p>转化率</p>
<p>{{ formatterRate((data.convMbrNum / data.touchMbrNum) * 100) }}</p>
</div>
</div>
<div class="item-bg bg-green center_flex min-w-210">
<div>
<p class="key">触达顾客转化人数<tip :text="tipText.convMbrNum" /></p>
<p class="value">{{ formatterNum(data.convMbrNum) }}</p>
</div>
</div>
<div class="item-bg bg-green min-w-433">
<div>
<p class="key">触达顾客订单数<tip :text="tipText.convOrderCnt" /></p>
<p class="value">{{ formatterNum(data.convOrderCnt) }}</p>
</div>
<div>
<p class="key">触达顾客转化收益(元)<tip :text="tipText.convSalesAmt" /></p>
<p class="value">{{ formatterNumAndFixed(data.convSalesAmt) }}</p>
</div>
</div>
</div>
</template>
<script>
import formatterNum from '@/mixins/validateNum';
import tip from '@/components/tip';
export default {
name: 'integral-traffic-sum',
components: { tip },
props: {
data: {
type: Object,
default: () => {}
}
},
data() {
return {
tipText: {
planMbrNum: '计划使用积分触达的顾客人数。如果当前计划在不同时间,通过不同区域(门店),用积分多次触达同一个顾客,会进行去重统计,仅计算为1个人。',
touchMbrNum: '实际使用积分触达的顾客人数。如果当前计划在不同时间,通过不同区域(门店),用积分多次触达同一个顾客,会进行去重统计,仅计算为1个人。',
convMbrNum: '使用积分触达的顾客中,在收益有效期内消费的顾客人数。无论顾客消费了几次,会进行去重统计,仅计算为1个人。',
convOrderCnt: '使用积分触达的顾客中,在收益有效期内消费的顾客订单数。',
convSalesAmt: '使用积分触达的顾客中,在收益有效期内消费的顾客订单金额。'
}
};
},
mixins: [formatterNum]
};
</script>
<style lang="scss" scoped>
.middle {
font-family: PingFangSC-Medium, PingFang SC;
height: 100px;
display: flex;
align-items: center;
justify-content: space-around;
.item-bg {
flex: 1;
height: 100%;
border-radius: 6px;
margin-right: 47px;
box-sizing: border-box;
position: relative;
&.bg-green {
background: #e3fff8;
}
&.bg-purper {
background: #f0f5ff;
}
.key {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
color: #606266;
line-height: 20px;
padding-top: 4px;
margin-bottom: 6px;
}
.value {
font-size: 24px;
font-family: DINAlternate-Bold, DINAlternate;
font-weight: bold;
color: #303133;
line-height: 28px;
}
}
.item-arrow {
width: 78px;
height: 65px;
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
padding-left: 7px;
box-sizing: border-box;
z-index: 3;
right: -77px;
&.purper {
background: url('~@/assets/img/arrow_purper.png');
background-size: 100% 100%;
}
&.green {
background: url('~@/assets/img/arrow_green.png');
background-size: 100% 100%;
}
p {
width: 47px;
font-size: 12px;
margin-top: -3px;
line-height: 20px;
color: #606266;
font-family: PingFangSC-Regular, PingFang SC;
&:nth-child(2) {
font-family: DINAlternate-Bold, DINAlternate;
font-weight: bold;
color: #303133;
}
}
}
}
.center_flex {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.min-w-173 {
min-width: 173px;
}
.min-w-210 {
min-width: 210px;
margin-right: 7px !important;
}
.min-w-433 {
min-width: 433px;
margin-right: 0 !important;
display: flex;
align-items: center;
padding: 0 50px 0 36px;
box-sizing: border-box;
div {
flex: 1;
text-align: left;
}
}
</style>
......@@ -8,7 +8,8 @@
<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" />
</div>
<card-profit v-if="cardLeadTable.length" :list="cardLead" :tableData="cardLeadTable" />
<card-profit v-if="cardLeadTable.length" :list="findTypeIsExist(9)" :tableData="cardLeadTable" />
<integral v-if="findTypeIsExist(8)" :data="findTypeObj(optionsList, 8)" />
<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)" />
......@@ -23,6 +24,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 integral from '@/views/ecm/touch-components/integral/index.vue';
import { ecmTouchEffectTable, ecmPlanTouchConfig, getCardLeads, getCardLeadsList, ecmTouchTypeTableList, getTouchType } from '@/service/api/ecmApi.js';
export default {
name: 'ecm',
......@@ -35,7 +37,7 @@ export default {
isReference: false,
batchNum: 0, // 批次数量
batchTime: '', // 批次时间
cardLead: {},
// cardLead: {},
cardLeadTable: [],
touchType: {},
getTouchTypeFlag: false,
......@@ -49,7 +51,8 @@ export default {
batchSend,
phoneTraffic,
shortMsg,
wechat
wechat,
integral
},
mounted() {
this.ecmPlanId = this.$route.params.id;
......@@ -58,8 +61,13 @@ export default {
this.getOtherList();
this.getMarketList();
this.getTouchConfig();
this.getCardLeadsSum();
this.getCardLeadTable();
Promise.all([this.getCardLeadsSum(), this.getCardLeadTable()]).then(([res1, res2]) => {
this.cardLeadTable = res1.result && res2.result.length > 1 ? [{ cardName: '合计(按照订单去重)', ...res1.result }] : [];
this.cardLeadTable = this.cardLeadTable.concat(res2.result || []);
console.log(res2.result);
});
// this.getCardLeadsSum();
// this.getCardLeadTable();
this.getTouchTypeList();
},
methods: {
......@@ -107,14 +115,10 @@ export default {
},
// 获取卡券收益数据
getCardLeadsSum() {
getCardLeads({ ecmPlanId: this.$route.params.id }).then(res => {
this.cardLead = res.result || {};
});
return getCardLeads({ ecmPlanId: this.$route.params.id });
},
getCardLeadTable() {
getCardLeadsList({ ecmPlanId: this.$route.params.id }).then(res => {
this.cardLeadTable = res.result || [];
});
return getCardLeadsList({ ecmPlanId: this.$route.params.id });
},
// 获取其他所有模块数据
getOtherList() {
......
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